//+------------------------------------------------------------------+ //| SecurityManager.mqh | //| Copyright 2025, EscapeEA | //| https://www.escapeea.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, EscapeEA" #property link "https://www.escapeea.com" #property version "1.10" // Added rate limiting and enhanced error handling #property strict #include #include #include "SecureMemory.mqh" #include "InputValidation.mqh" // Security event categories enum ENUM_SECURITY_EVENT_CATEGORY { SECURITY_CATEGORY_AUTH, // Authentication/Authorization SECURITY_CATEGORY_TRADE, // Trading operations SECURITY_CATEGORY_FILE, // File operations SECURITY_CATEGORY_NETWORK, // Network operations SECURITY_CATEGORY_SYSTEM // System operations }; //--- Security levels enum ENUM_SECURITY_LEVEL { SECURITY_LEVEL_CRITICAL = 0, // Critical security issue - stop trading SECURITY_LEVEL_HIGH = 1, // High risk - log and potentially block SECURITY_LEVEL_MEDIUM = 2, // Medium risk - log and warn SECURITY_LEVEL_LOW = 3, // Low risk - log only SECURITY_LEVEL_INFO = 4, // Informational messages SECURITY_LEVEL_DEBUG = 5 // Debug information }; // Alias for backward compatibility #define SECURITY_LEVEL_INFO SECURITY_LEVEL_DEBUG //--- Security event structure struct SSecurityEvent { datetime time; // Time of the event string source; // Source of the event string message; // Event message ENUM_SECURITY_LEVEL level; // Security level ENUM_SECURITY_EVENT_CATEGORY category; // Event category int errorCode; // Error code if any string details; // Additional details }; //+------------------------------------------------------------------+ //| Security Manager class | //+------------------------------------------------------------------+ class CSecurityManager { private: CAccountInfo m_account; // Account info CSymbolInfo m_symbol; // Symbol info // Security settings bool m_enableDllImportCheck; // Check for unauthorized DLL imports bool m_enableEnvironmentCheck; // Check environment integrity bool m_enableTradeValidation; // Validate trades before execution // Internal state bool m_isInitialized; string m_logFilePath; // Prevent copying CSecurityManager(const CSecurityManager&); void operator=(const CSecurityManager&); // Private constructor for singleton pattern CSecurityManager() : m_isInitialized(false) {} // Rate limiting for security events struct RateLimitEntry { int count; datetime lastReset; int maxEvents; int timeWindow; // in seconds }; RateLimitEntry m_rateLimits[]; // Initialize rate limits void InitializeRateLimits() { // Default rate limits (events per minute) ArrayResize(m_rateLimits, 3); // Critical events (10 per hour) m_rateLimits[0].count = 0; m_rateLimits[0].lastReset = TimeCurrent(); m_rateLimits[0].maxEvents = 10; m_rateLimits[0].timeWindow = 3600; // 1 hour // High priority events (30 per hour) m_rateLimits[1].count = 0; m_rateLimits[1].lastReset = TimeCurrent(); m_rateLimits[1].maxEvents = 30; m_rateLimits[1].timeWindow = 3600; // 1 hour // Normal events (100 per hour) m_rateLimits[2].count = 0; m_rateLimits[2].lastReset = TimeCurrent(); m_rateLimits[2].maxEvents = 100; m_rateLimits[2].timeWindow = 3600; // 1 hour } // Check if event should be logged based on rate limiting bool CheckRateLimit(ENUM_SECURITY_LEVEL level) { int index = 2; // Default to normal events if(level == SECURITY_LEVEL_CRITICAL) index = 0; else if(level == SECURITY_LEVEL_HIGH) index = 1; if(index < 0 || index >= ArraySize(m_rateLimits)) return true; datetime currentTime = TimeCurrent(); if(currentTime - m_rateLimits[index].lastReset > m_rateLimits[index].timeWindow) { m_rateLimits[index].count = 0; m_rateLimits[index].lastReset = currentTime; } if(m_rateLimits[index].count >= m_rateLimits[index].maxEvents) { return false; } m_rateLimits[index].count++; return true; } // Log security event to file with rate limiting void LogSecurityEvent(const SSecurityEvent &event) { // Skip logging if rate limit exceeded (except for critical events) if(event.level > SECURITY_LEVEL_CRITICAL && !CheckRateLimit(event.level)) { return; } string levelText; switch(event.level) { case SECURITY_LEVEL_CRITICAL: levelText = "CRITICAL"; break; case SECURITY_LEVEL_HIGH: levelText = "HIGH "; break; case SECURITY_LEVEL_MEDIUM: levelText = "MEDIUM "; break; case SECURITY_LEVEL_LOW: levelText = "LOW "; break; case SECURITY_LEVEL_DEBUG: levelText = "DEBUG "; break; default: levelText = "UNKNOWN "; break; } string categoryText; switch(event.category) { case SECURITY_CATEGORY_AUTH: categoryText = "AUTH "; break; case SECURITY_CATEGORY_TRADE: categoryText = "TRADE "; break; case SECURITY_CATEGORY_FILE: categoryText = "FILE "; break; case SECURITY_CATEGORY_NETWORK: categoryText = "NETWORK "; break; case SECURITY_CATEGORY_SYSTEM: categoryText = "SYSTEM "; break; default: categoryText = "UNKNOWN "; break; } string logEntry = StringFormat("[%s] [%s] [%s] %s: %s (Code: %d, Details: %s)", TimeToString(event.time, TIME_DATE|TIME_SECONDS|TIME_MINUTES|TIME_SECONDS), levelText, categoryText, event.source, event.message, event.errorCode, event.details); // Print to journal with appropriate verbosity if(event.level <= SECURITY_LEVEL_MEDIUM) { Print(logEntry); } // Always log to file if path is set if(m_logFilePath != "") { int fileHandle = FileOpen(m_logFilePath, FILE_READ|FILE_WRITE|FILE_TXT|FILE_ANSI); if(fileHandle != INVALID_HANDLE) { FileSeek(fileHandle, 0, SEEK_END); FileWriteString(fileHandle, logEntry + "\n"); FileFlush(fileHandle); FileClose(fileHandle); } else { // If we can't log to file, at least print to journal Print("Failed to write to security log: ", GetLastError()); } } } public: // Get singleton instance static CSecurityManager* Instance() { static CSecurityManager instance; return &instance; } // Initialize security manager bool Initialize(const string symbol = "", const string logPath = "") { if(m_isInitialized) return true; // Set up logging with validation if(logPath != "") { if(!ExtInputValidator.IsValidFilePath(logPath)) { Print("Invalid log file path: ", logPath); return false; } m_logFilePath = logPath; } // Initialize rate limits InitializeRateLimits(); // Initialize account and symbol info if(symbol != "") { if(!m_symbol.Name(symbol)) { Print("Failed to set symbol for security manager"); return false; } } // Enable all security checks by default m_enableDllImportCheck = true; m_enableEnvironmentCheck = true; m_enableTradeValidation = true; m_isInitialized = true; return true; } // Validate the trading environment bool ValidateEnvironment() { bool isValid = true; int errorCode = 0; // 1. Check if terminal is allowed to trade if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { errorCode = GetLastError(); LogEvent(SECURITY_LEVEL_CRITICAL, SECURITY_CATEGORY_TRADE, "Environment", "AutoTrading is disabled in terminal settings", errorCode, "Trading operations will be blocked"); isValid = false; } // 2. Check if DLL imports are allowed if(TerminalInfoInteger(TERMINAL_DLLS_ALLOWED) == 0) { LogEvent(SECURITY_LEVEL_HIGH, SECURITY_CATEGORY_SYSTEM, "Environment", "DLL imports are disabled in terminal settings", 0, "Some functionality may be limited"); } // 3. Check if file operations are allowed if(TerminalInfoInteger(TERMINAL_CODEPAGE) == 0) { LogEvent(SECURITY_LEVEL_MEDIUM, SECURITY_CATEGORY_FILE, "Environment", "File operations may be restricted", 0, "Check terminal permissions"); } // 4. Check for terminal updates if(!TerminalInfoInteger(TERMINAL_CONNECTED)) { LogEvent(SECURITY_LEVEL_MEDIUM, SECURITY_CATEGORY_NETWORK, "Environment", "Terminal is not connected to the internet", 0, "Some features may not work as expected"); } // 5. Check for running in strategy tester if(MQLInfoInteger(MQL_TESTER)) { LogEvent(SECURITY_LEVEL_DEBUG, SECURITY_CATEGORY_SYSTEM, "Environment", "Running in Strategy Tester", 0, "Some security checks may be bypassed"); } // 6. Check for running in visual mode if(MQLInfoInteger(MQL_VISUAL_MODE)) { LogEvent(SECURITY_LEVEL_DEBUG, SECURITY_CATEGORY_SYSTEM, "Environment", "Running in visual mode", 0, "Performance may be affected"); } // 7. Check for sufficient permissions if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { errorCode = GetLastError(); LogEvent(SECURITY_LEVEL_CRITICAL, SECURITY_CATEGORY_AUTH, "Permissions", "Insufficient trading permissions", errorCode, "Check terminal settings and account permissions"); isValid = false; } // 8. Check for account permissions if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED)) { errorCode = GetLastError(); LogEvent(SECURITY_LEVEL_CRITICAL, SECURITY_CATEGORY_AUTH, "Account", "Trading is not allowed for this account", errorCode, "Check account settings"); isValid = false; } // 9. Check for demo account if required if(AccountInfoInteger(ACCOUNT_TRADE_MODE) == ACCOUNT_TRADE_MODE_DEMO) { LogEvent(SECURITY_LEVEL_INFO, SECURITY_CATEGORY_TRADE, "Account", "Running on demo account", 0, "No real funds are at risk"); } // 10. Log environment summary if(isValid) { LogEvent(SECURITY_LEVEL_DEBUG, SECURITY_CATEGORY_SYSTEM, "Environment", "Environment validation passed", 0, "All required permissions and settings are valid"); } else { LogEvent(SECURITY_LEVEL_CRITICAL, SECURITY_CATEGORY_SYSTEM, "Environment", "Environment validation failed", 0, "See previous messages for details"); } // Check if symbol is selected and valid if(m_symbol.Name() != "") { // Use the correct Select method signature for CSymbolInfo if(!m_symbol.Name(m_symbol.Name())) { SSecurityEvent event = {0}; event.time = TimeCurrent(); event.source = "Symbol"; event.message = "Failed to select symbol: " + m_symbol.Name(); event.level = SECURITY_LEVEL_CRITICAL; event.errorCode = GetLastError(); LogSecurityEvent(event); isValid = false; } else { // Update symbol rates and refresh after selection m_symbol.RefreshRates(); } } return isValid; } // Get current security level ENUM_SECURITY_LEVEL GetSecurityLevel() { // Default to high security level ENUM_SECURITY_LEVEL level = SECURITY_LEVEL_LOW; // Check for critical issues first if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || !m_account.TradeAllowed()) { return SECURITY_LEVEL_CRITICAL; } // Check for high-priority issues if(!TerminalInfoInteger(TERMINAL_DLLS_ALLOWED) && m_enableDllImportCheck) { level = (ENUM_SECURITY_LEVEL)MathMin(level, SECURITY_LEVEL_HIGH); } // Add more checks as needed return level; } // Log a security event with category and details void LogEvent(ENUM_SECURITY_LEVEL level, ENUM_SECURITY_EVENT_CATEGORY category, const string source, const string message, int errorCode = 0, const string details = "") { SSecurityEvent event = {0}; event.time = TimeCurrent(); event.source = source; event.message = message; event.level = level; event.category = category; event.errorCode = errorCode; event.details = details; LogSecurityEvent(event); } // Clean up resources void Shutdown() { m_isInitialized = false; } // Destructor ~CSecurityManager() { Shutdown(); } }; // Global instance CSecurityManager* ExtSecurity = NULL;