mql5/Include/Escape/SecurityManager.mqh

379 lines
14 KiB
MQL5
Raw Permalink Normal View History

2025-08-05 01:57:33 -04:00
//+------------------------------------------------------------------+
//| 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 <Trade\AccountInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#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;