379 lines
14 KiB
MQL5
379 lines
14 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| 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;
|