Skip to main content
Glama
audit-logger.js•12.9 kB
import winston from 'winston'; import crypto from 'crypto'; import fs from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); /** * Comprehensive audit logging for security and compliance */ export class AuditLogger { constructor() { this.auditLog = this.initializeAuditLogger(); this.securityLog = this.initializeSecurityLogger(); this.tradingLog = this.initializeTradingLogger(); this.logQueue = []; this.logIntegrity = new Map(); } /** * Initialize audit logger */ initializeAuditLogger() { return winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json(), winston.format.printf(info => { const log = { timestamp: info.timestamp, level: info.level, category: 'AUDIT', ...info }; // Add integrity hash log.hash = this.calculateLogHash(log); return JSON.stringify(log); }) ), transports: [ new winston.transports.File({ filename: path.join(__dirname, '../../logs/audit.log'), maxsize: 10485760, // 10MB maxFiles: 100, tailable: true }), new winston.transports.Console({ level: 'error' }) ] }); } /** * Initialize security event logger */ initializeSecurityLogger() { return winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.File({ filename: path.join(__dirname, '../../logs/security.log'), maxsize: 10485760, maxFiles: 50 }) ] }); } /** * Initialize trading activity logger */ initializeTradingLogger() { return winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.File({ filename: path.join(__dirname, '../../logs/trading.log'), maxsize: 10485760, maxFiles: 50 }) ] }); } /** * Log authentication events */ logAuthentication(event) { const logEntry = { eventType: 'AUTHENTICATION', action: event.action, // LOGIN, LOGOUT, FAILED_LOGIN userId: event.userId, identifier: this.maskSensitive(event.identifier), ipAddress: event.ipAddress, userAgent: event.userAgent, success: event.success, reason: event.reason, mfaUsed: event.mfaUsed || false, sessionId: event.sessionId, timestamp: new Date().toISOString() }; this.auditLog.info(logEntry); // Alert on suspicious activity if (event.action === 'FAILED_LOGIN') { this.trackFailedLogin(event.identifier, event.ipAddress); } } /** * Log trading operations */ logTrading(event) { const logEntry = { eventType: 'TRADING', action: event.action, // CREATE_POSITION, CLOSE_POSITION, etc. userId: event.userId, dealId: event.dealId, epic: event.epic, direction: event.direction, size: event.size, price: event.price, profit: event.profit, status: event.status, reason: event.reason, timestamp: new Date().toISOString() }; this.tradingLog.info(logEntry); this.auditLog.info(logEntry); // Alert on large trades if (event.size > 1000 || Math.abs(event.profit || 0) > 10000) { this.alertLargeTrade(logEntry); } } /** * Log data access events */ logDataAccess(event) { const logEntry = { eventType: 'DATA_ACCESS', action: event.action, // VIEW, EXPORT, MODIFY userId: event.userId, resource: event.resource, resourceId: event.resourceId, ipAddress: event.ipAddress, success: event.success, dataSize: event.dataSize, timestamp: new Date().toISOString() }; this.auditLog.info(logEntry); // Track sensitive data access if (event.resource === 'ACCOUNT_DETAILS' || event.resource === 'TRADING_HISTORY') { this.trackSensitiveAccess(logEntry); } } /** * Log security events */ logSecurity(event) { const logEntry = { eventType: 'SECURITY', severity: event.severity, // LOW, MEDIUM, HIGH, CRITICAL category: event.category, // INTRUSION, VIOLATION, ANOMALY description: event.description, userId: event.userId, ipAddress: event.ipAddress, details: event.details, mitigation: event.mitigation, timestamp: new Date().toISOString() }; this.securityLog.info(logEntry); if (event.severity === 'HIGH' || event.severity === 'CRITICAL') { this.alertSecurityTeam(logEntry); } } /** * Log API calls */ logApiCall(event) { const logEntry = { eventType: 'API_CALL', method: event.method, path: event.path, userId: event.userId, ipAddress: event.ipAddress, statusCode: event.statusCode, responseTime: event.responseTime, errorMessage: event.errorMessage, rateLimit: event.rateLimit, timestamp: new Date().toISOString() }; this.auditLog.info(logEntry); // Track API abuse if (event.statusCode === 429 || event.rateLimit?.remaining === 0) { this.trackApiAbuse(event.userId, event.ipAddress); } } /** * Log configuration changes */ logConfigChange(event) { const logEntry = { eventType: 'CONFIG_CHANGE', action: event.action, userId: event.userId, setting: event.setting, oldValue: this.maskSensitive(event.oldValue), newValue: this.maskSensitive(event.newValue), reason: event.reason, approved: event.approved, approvedBy: event.approvedBy, timestamp: new Date().toISOString() }; this.auditLog.info(logEntry); this.securityLog.info(logEntry); } /** * Track failed login attempts */ failedLogins = new Map(); trackFailedLogin(identifier, ipAddress) { const key = `${identifier}:${ipAddress}`; const attempts = this.failedLogins.get(key) || []; attempts.push(Date.now()); // Keep only attempts in last hour const recentAttempts = attempts.filter(t => Date.now() - t < 3600000); this.failedLogins.set(key, recentAttempts); // Alert on brute force attempts if (recentAttempts.length >= 5) { this.logSecurity({ severity: 'HIGH', category: 'INTRUSION', description: 'Possible brute force attack detected', userId: identifier, ipAddress: ipAddress, details: { attempts: recentAttempts.length }, mitigation: 'Account temporarily locked' }); } } /** * Track sensitive data access */ sensitiveAccess = new Map(); trackSensitiveAccess(logEntry) { const key = logEntry.userId; const accesses = this.sensitiveAccess.get(key) || []; accesses.push({ resource: logEntry.resource, timestamp: logEntry.timestamp }); // Keep only last 24 hours const recentAccesses = accesses.filter(a => Date.now() - new Date(a.timestamp).getTime() < 86400000 ); this.sensitiveAccess.set(key, recentAccesses); // Alert on unusual access patterns if (recentAccesses.length > 100) { this.logSecurity({ severity: 'MEDIUM', category: 'ANOMALY', description: 'Unusual data access pattern detected', userId: key, details: { accessCount: recentAccesses.length } }); } } /** * Track API abuse */ apiAbuse = new Map(); trackApiAbuse(userId, ipAddress) { const key = `${userId}:${ipAddress}`; const violations = this.apiAbuse.get(key) || 0; this.apiAbuse.set(key, violations + 1); if (violations >= 3) { this.logSecurity({ severity: 'MEDIUM', category: 'VIOLATION', description: 'API rate limit violations', userId: userId, ipAddress: ipAddress, details: { violations: violations + 1 }, mitigation: 'Consider blocking IP' }); } } /** * Alert on large trades */ alertLargeTrade(trade) { this.logSecurity({ severity: 'LOW', category: 'MONITORING', description: 'Large trade executed', userId: trade.userId, details: { dealId: trade.dealId, size: trade.size, profit: trade.profit } }); } /** * Alert security team */ async alertSecurityTeam(event) { // In production, this would send alerts via email, SMS, Slack, etc. console.error('🚨 SECURITY ALERT:', event); // Write to critical events file const criticalLog = path.join(__dirname, '../../logs/critical.log'); await fs.appendFile(criticalLog, JSON.stringify(event) + '\n'); } /** * Calculate log hash for integrity */ calculateLogHash(log) { const hash = crypto.createHash('sha256'); hash.update(JSON.stringify(log)); return hash.digest('hex'); } /** * Verify log integrity */ async verifyLogIntegrity(logFile) { try { const content = await fs.readFile(logFile, 'utf8'); const lines = content.split('\n').filter(l => l.trim()); let valid = 0; let invalid = 0; for (const line of lines) { try { const log = JSON.parse(line); const expectedHash = log.hash; delete log.hash; const actualHash = this.calculateLogHash(log); if (expectedHash === actualHash) { valid++; } else { invalid++; this.logSecurity({ severity: 'CRITICAL', category: 'VIOLATION', description: 'Log tampering detected', details: { logFile, line: log } }); } } catch (e) { invalid++; } } return { valid, invalid, total: lines.length }; } catch (error) { throw new Error(`Failed to verify log integrity: ${error.message}`); } } /** * Mask sensitive information */ maskSensitive(value) { if (!value) return value; const str = String(value); // Mask API keys if (str.length > 20 && /^[A-Za-z0-9]+$/.test(str)) { return str.substring(0, 4) + '****' + str.substring(str.length - 4); } // Mask email addresses if (str.includes('@')) { const [local, domain] = str.split('@'); return local.substring(0, 2) + '***@' + domain; } // Mask other potentially sensitive data if (str.length > 10) { return str.substring(0, 3) + '***' + str.substring(str.length - 3); } return value; } /** * Generate audit report */ async generateAuditReport(startDate, endDate) { const report = { period: { start: startDate, end: endDate }, authentication: { successful: 0, failed: 0, uniqueUsers: new Set() }, trading: { positions: 0, totalVolume: 0, totalProfit: 0 }, security: { incidents: [], violations: 0 }, api: { totalCalls: 0, errors: 0, averageResponseTime: 0 } }; // Process logs and generate report // This would read through log files and compile statistics return report; } /** * Clean old logs */ async cleanOldLogs(daysToKeep = 90) { const logsDir = path.join(__dirname, '../../logs'); const files = await fs.readdir(logsDir); const cutoffTime = Date.now() - (daysToKeep * 24 * 60 * 60 * 1000); for (const file of files) { const filePath = path.join(logsDir, file); const stats = await fs.stat(filePath); if (stats.mtime.getTime() < cutoffTime) { // Archive before deletion await this.archiveLog(filePath); await fs.unlink(filePath); } } } /** * Archive log file */ async archiveLog(logFile) { // In production, this would upload to secure storage (S3, etc.) const archiveDir = path.join(__dirname, '../../logs/archive'); await fs.mkdir(archiveDir, { recursive: true }); const basename = path.basename(logFile); const timestamp = new Date().toISOString().replace(/:/g, '-'); const archivePath = path.join(archiveDir, `${timestamp}_${basename}.gz`); // Compress and move file // Implementation would use zlib for compression } } export const auditLogger = new AuditLogger();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kea0811/ig-trading-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server