Skip to main content
Glama
audit.js4.76 kB
// audit.js - Audit Logging System const fs = require('fs').promises; const path = require('path'); const config = require('./config'); class AuditLogger { constructor() { this.logFile = config.audit.logFile; this.enabled = config.audit.enabled; this.queue = []; this.isWriting = false; this.init(); } async init() { if (!this.enabled) { console.error('[AUDIT] Audit logging disabled'); return; } try { // Ensure log directory exists const logDir = path.dirname(this.logFile); await fs.mkdir(logDir, { recursive: true }); console.error(`[AUDIT] Audit logging enabled: ${this.logFile}`); // Write startup entry await this.log('SYSTEM', 'startup', { version: config.server.version, environment: config.server.environment }); } catch (error) { console.error(`[AUDIT] Failed to initialize audit log: ${error.message}`); } } /** * Log an audit event */ async log(action, resource, details = {}, userId = 'system', severity = 'info') { if (!this.enabled) return; const entry = { timestamp: new Date().toISOString(), action, resource, userId, severity, details: typeof details === 'object' ? details : { message: String(details) }, pid: process.pid }; // Add to queue this.queue.push(entry); // Process queue await this.processQueue(); } /** * Process the log queue */ async processQueue() { if (this.isWriting || this.queue.length === 0) return; this.isWriting = true; try { // Get all pending entries const entries = [...this.queue]; this.queue = []; // Format entries as JSON lines const lines = entries.map(entry => JSON.stringify(entry)).join('\n') + '\n'; // Append to log file await fs.appendFile(this.logFile, lines, 'utf8'); // Also log to stderr for real-time monitoring entries.forEach(entry => { console.error(`[AUDIT] ${entry.severity.toUpperCase()} - ${entry.action}: ${entry.resource}`); }); } catch (error) { console.error(`[AUDIT] Failed to write audit log: ${error.message}`); // Don't lose the entries, put them back this.queue.unshift(...entries); } finally { this.isWriting = false; // Process any new entries that arrived while writing if (this.queue.length > 0) { await this.processQueue(); } } } /** * Log successful tool execution */ async logToolExecution(toolName, args, userId = 'unknown', duration = 0) { await this.log( 'TOOL_EXECUTION', toolName, { arguments: args, durationMs: duration, success: true }, userId, 'info' ); } /** * Log failed tool execution */ async logToolError(toolName, args, error, userId = 'unknown') { await this.log( 'TOOL_ERROR', toolName, { arguments: args, error: error.message, errorCode: error.code }, userId, 'error' ); } /** * Log security events */ async logSecurityEvent(eventType, details, userId = 'unknown') { await this.log( 'SECURITY_EVENT', eventType, details, userId, 'warning' ); } /** * Log rate limit violations */ async logRateLimitViolation(userId, resource) { await this.log( 'RATE_LIMIT_VIOLATION', resource, { message: 'Rate limit exceeded' }, userId, 'warning' ); } /** * Log credential extraction (high severity) */ async logCredentialExtraction(userId, pcapPath, credentialCount) { await this.log( 'CREDENTIAL_EXTRACTION', pcapPath, { credentialsFound: credentialCount, warning: 'Sensitive operation performed' }, userId, 'high' ); } /** * Get recent audit entries */ async getRecentEntries(count = 100) { if (!this.enabled) { return []; } try { const content = await fs.readFile(this.logFile, 'utf8'); const lines = content.trim().split('\n'); const entries = lines .slice(-count) .map(line => { try { return JSON.parse(line); } catch { return null; } }) .filter(entry => entry !== null); return entries.reverse(); } catch (error) { console.error(`[AUDIT] Failed to read audit log: ${error.message}`); return []; } } /** * Flush any pending log entries */ async flush() { await this.processQueue(); } } // Export singleton instance module.exports = 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/anishphilip012git/WireMCP-Secure'

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