Continuous Monitoring Implementation

Continuous Monitoring Implementation

Real-time Security Header Monitoring System

const EventEmitter = require('events');

class SecurityHeaderMonitor extends EventEmitter {
    constructor(config) {
        super();
        this.config = config;
        this.endpoints = config.endpoints || [];
        this.interval = config.interval || 300000; // 5 minutes
        this.thresholds = config.thresholds || {};
        this.results = new Map();
    }
    
    start() {
        console.log('Starting security header monitoring...');
        this.check(); // Initial check
        this.timer = setInterval(() => this.check(), this.interval);
    }
    
    stop() {
        if (this.timer) {
            clearInterval(this.timer);
            console.log('Monitoring stopped');
        }
    }
    
    async check() {
        for (const endpoint of this.endpoints) {
            try {
                const result = await this.checkEndpoint(endpoint);
                this.processResult(endpoint, result);
            } catch (error) {
                this.emit('error', { endpoint, error });
            }
        }
    }
    
    async checkEndpoint(endpoint) {
        const response = await axios.get(endpoint.url, {
            timeout: 10000,
            validateStatus: () => true
        });
        
        const headers = response.headers;
        const statusCode = response.status;
        
        return {
            timestamp: new Date(),
            statusCode,
            headers: this.extractSecurityHeaders(headers),
            violations: this.checkViolations(headers, endpoint.expectedHeaders),
            score: this.calculateScore(headers)
        };
    }
    
    extractSecurityHeaders(headers) {
        const securityHeaders = [
            'content-security-policy',
            'x-content-type-options',
            'x-frame-options',
            'strict-transport-security',
            'referrer-policy',
            'permissions-policy'
        ];
        
        return securityHeaders.reduce((acc, header) => {
            if (headers[header]) {
                acc[header] = headers[header];
            }
            return acc;
        }, {});
    }
    
    checkViolations(headers, expectedHeaders = {}) {
        const violations = [];
        
        Object.entries(expectedHeaders).forEach(([header, expected]) => {
            const actual = headers[header];
            
            if (!actual) {
                violations.push({
                    type: 'missing',
                    header,
                    expected
                });
            } else if (actual !== expected && !this.isValidVariation(header, actual, expected)) {
                violations.push({
                    type: 'mismatch',
                    header,
                    expected,
                    actual
                });
            }
        });
        
        return violations;
    }
    
    processResult(endpoint, result) {
        const previous = this.results.get(endpoint.url);
        this.results.set(endpoint.url, result);
        
        // Check for changes
        if (previous) {
            const changes = this.detectChanges(previous, result);
            if (changes.length > 0) {
                this.emit('change', { endpoint, changes, result });
            }
        }
        
        // Check for violations
        if (result.violations.length > 0) {
            this.emit('violation', { endpoint, violations: result.violations, result });
        }
        
        // Check score threshold
        if (result.score < (this.thresholds.minScore || 80)) {
            this.emit('low-score', { endpoint, score: result.score, result });
        }
    }
    
    detectChanges(previous, current) {
        const changes = [];
        
        // Check for added/removed headers
        const prevHeaders = Object.keys(previous.headers);
        const currHeaders = Object.keys(current.headers);
        
        currHeaders.forEach(header => {
            if (!prevHeaders.includes(header)) {
                changes.push({ type: 'added', header, value: current.headers[header] });
            }
        });
        
        prevHeaders.forEach(header => {
            if (!currHeaders.includes(header)) {
                changes.push({ type: 'removed', header });
            }
        });
        
        // Check for modified headers
        currHeaders.forEach(header => {
            if (prevHeaders.includes(header) && 
                previous.headers[header] !== current.headers[header]) {
                changes.push({
                    type: 'modified',
                    header,
                    oldValue: previous.headers[header],
                    newValue: current.headers[header]
                });
            }
        });
        
        return changes;
    }
    
    calculateScore(headers) {
        let score = 100;
        const penalties = {
            missingCSP: 20,
            missingHSTS: 15,
            missingXFrameOptions: 10,
            missingXContentTypeOptions: 10,
            unsafeCSP: 15,
            shortHSTS: 10
        };
        
        if (!headers['content-security-policy']) {
            score -= penalties.missingCSP;
        } else if (headers['content-security-policy'].includes('unsafe-inline')) {
            score -= penalties.unsafeCSP;
        }
        
        if (!headers['strict-transport-security']) {
            score -= penalties.missingHSTS;
        } else {
            const maxAge = headers['strict-transport-security'].match(/max-age=(\d+)/);
            if (maxAge && parseInt(maxAge[1]) < 31536000) {
                score -= penalties.shortHSTS;
            }
        }
        
        if (!headers['x-frame-options']) score -= penalties.missingXFrameOptions;
        if (!headers['x-content-type-options']) score -= penalties.missingXContentTypeOptions;
        
        return Math.max(0, score);
    }
}

// Usage with alerting
const monitor = new SecurityHeaderMonitor({
    endpoints: [
        {
            url: 'https://example.com',
            expectedHeaders: {
                'x-content-type-options': 'nosniff',
                'x-frame-options': 'DENY'
            }
        },
        {
            url: 'https://api.example.com',
            expectedHeaders: {
                'content-security-policy': "default-src 'self'"
            }
        }
    ],
    interval: 300000, // 5 minutes
    thresholds: {
        minScore: 85
    }
});

// Set up event handlers
monitor.on('violation', ({ endpoint, violations }) => {
    console.error(`Security header violations detected at ${endpoint.url}:`, violations);
    // Send alert to monitoring system
});

monitor.on('change', ({ endpoint, changes }) => {
    console.warn(`Security headers changed at ${endpoint.url}:`, changes);
    // Log to audit system
});

monitor.on('low-score', ({ endpoint, score }) => {
    console.error(`Low security score (${score}) at ${endpoint.url}`);
    // Trigger incident response
});

monitor.start();