Newer
Older
express-blog / public / js / forensicTracker.js
// Balanced forensic tracker - focused on threat detection
(function() {
    'use strict';

    // Configuration
    const config = {
        enableBehaviorTracking: true,
        enableFingerprinting: true,
        enableNetworkDetection: true,
        maxDataSize: 10000, // Limit data collection
        debugMode: false
    };

    // Data collection object
    const forensicData = {
        timestamp: Date.now(),
        sessionId: generateSessionId(),
        pageLoadTime: Date.now(),
        formStartTime: null,
        formCompletionTime: null,
        device: {},
        network: {},
        behavior: {},
        security: {}
    };

    // Generate session ID
    function generateSessionId() {
        return 'sess_' + Math.random().toString(36).substr(2, 9) + '_' + Date.now();
    }

    // Basic device fingerprinting (less invasive)
    function collectDeviceInfo() {
        try {
            forensicData.device = {
                userAgent: navigator.userAgent,
                platform: navigator.platform,
                language: navigator.language,
                languages: navigator.languages,
                cookieEnabled: navigator.cookieEnabled,
                onLine: navigator.onLine,
                screen: {
                    width: screen.width,
                    height: screen.height,
                    colorDepth: screen.colorDepth,
                    pixelDepth: screen.pixelDepth
                },
                timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
                timezoneOffset: new Date().getTimezoneOffset(),
                // Basic hardware info (less detailed than before)
                hardwareConcurrency: navigator.hardwareConcurrency || 0,
                memory: navigator.deviceMemory || 0,
                // Remove canvas fingerprinting - too invasive
                // Keep simple WebGL detection
                webgl: !!window.WebGLRenderingContext,
                webgl2: !!window.WebGL2RenderingContext
            };
        } catch (e) {
            console.warn('Device info collection failed:', e);
        }
    }

    // Network detection (simplified)
    function detectNetworkInfo() {
        try {
            // Basic connection info
            if (navigator.connection) {
                forensicData.network.connection = {
                    effectiveType: navigator.connection.effectiveType,
                    type: navigator.connection.type,
                    downlink: navigator.connection.downlink,
                    rtt: navigator.connection.rtt,
                    saveData: navigator.connection.saveData
                };
            }

            // Simplified WebRTC detection (just check for VPN/proxy indicators)
            if (config.enableNetworkDetection && window.RTCPeerConnection) {
                detectWebRTCInfo();
            }
        } catch (e) {
            console.warn('Network detection failed:', e);
        }
    }

    // Simplified WebRTC detection
    function detectWebRTCInfo() {
        try {
            const pc = new RTCPeerConnection({
                iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
            });

            pc.createDataChannel('test');
            pc.createOffer().then(offer => pc.setLocalDescription(offer));

            const timeout = setTimeout(() => {
                pc.close();
                forensicData.network.webrtc = { status: 'timeout' };
            }, 3000);

            pc.onicecandidate = function(event) {
                if (event.candidate) {
                    const candidate = event.candidate.candidate;
                    if (candidate.includes('192.168.') || candidate.includes('10.') || candidate.includes('172.')) {
                        forensicData.network.webrtc = { 
                            hasLocalIP: true,
                            candidate: candidate.substring(0, 50) // Limit data
                        };
                    }
                    clearTimeout(timeout);
                    pc.close();
                }
            };
        } catch (e) {
            forensicData.network.webrtc = { error: 'WebRTC unavailable' };
        }
    }

    // Behavior tracking (focused on form interaction)
    function trackBehavior() {
        let focusCount = 0;
        let keystrokes = 0;
        let mouseClicks = 0;
        let formInteractions = [];

        const form = document.getElementById('contactForm');
        if (!form) return;

        // Track form focus events
        form.addEventListener('focusin', function(e) {
            focusCount++;
            if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
                formInteractions.push({
                    type: 'focus',
                    field: e.target.name || e.target.id,
                    timestamp: Date.now() - forensicData.pageLoadTime
                });
            }
        });

        // Track form submission timing
        form.addEventListener('submit', function() {
            forensicData.formCompletionTime = Date.now();
            forensicData.behavior.formTime = forensicData.formCompletionTime - (forensicData.formStartTime || forensicData.pageLoadTime);
        });

        // Track first form interaction
        form.addEventListener('input', function() {
            if (!forensicData.formStartTime) {
                forensicData.formStartTime = Date.now();
            }
            keystrokes++;
        });

        // Track clicks (limited)
        document.addEventListener('click', function() {
            mouseClicks++;
        });

        // Store behavior data
        forensicData.behavior = {
            focusCount: focusCount,
            keystrokes: keystrokes,
            mouseClicks: mouseClicks,
            formInteractions: formInteractions.slice(-10), // Limit to last 10
            pageTime: 0 // Will be calculated on submit
        };
    }

    // Security checks
    function performSecurityChecks() {
        forensicData.security = {
            // Check for automation indicators
            webdriver: !!navigator.webdriver,
            selenium: !!window.selenium,
            phantom: !!window.phantom,
            nightmare: !!window.nightmare,
            
            // Check for debugging
            devtools: false, // Will be set by devtools detection
            
            // Check for common bot indicators
            plugins: navigator.plugins.length,
            mimeTypes: navigator.mimeTypes.length,
            
            // Check for headless indicators
            headless: isHeadless()
        };

        // Simple devtools detection
        detectDevTools();
    }

    // Detect headless browsers
    function isHeadless() {
        return (
            navigator.webdriver ||
            !navigator.languages ||
            navigator.languages.length === 0 ||
            /HeadlessChrome/.test(navigator.userAgent) ||
            (!navigator.permissions && !navigator.serviceWorker && !navigator.clipboard)
        );
    }

    // Simple devtools detection
    function detectDevTools() {
        let devtools = false;
        const element = new Image();
        Object.defineProperty(element, 'id', {
            get: function() {
                devtools = true;
                return 'devtools-detected';
            }
        });
        
        setTimeout(() => {
            console.log(element);
            forensicData.security.devtools = devtools;
        }, 100);
    }

    // Main collection function
    function collectForensicData() {
        collectDeviceInfo();
        detectNetworkInfo();
        
        if (config.enableBehaviorTracking) {
            trackBehavior();
        }
        
        performSecurityChecks();
    }

    // Inject data into form submission
    function injectForensicData() {
        const form = document.getElementById('contactForm');
        if (!form) return;

        form.addEventListener('submit', function(e) {
            // Calculate final timing
            forensicData.behavior.pageTime = Date.now() - forensicData.pageLoadTime;
            
            // Limit data size
            const dataString = JSON.stringify(forensicData);
            if (dataString.length > config.maxDataSize) {
                console.warn('Forensic data too large, truncating');
                // Keep only essential data
                const essentialData = {
                    timestamp: forensicData.timestamp,
                    sessionId: forensicData.sessionId,
                    device: {
                        userAgent: forensicData.device.userAgent,
                        platform: forensicData.device.platform,
                        language: forensicData.device.language
                    },
                    behavior: {
                        formTime: forensicData.behavior.formTime,
                        pageTime: forensicData.behavior.pageTime
                    },
                    security: forensicData.security
                };
                
                // Create or update hidden field
                let hiddenField = document.getElementById('forensicData');
                if (!hiddenField) {
                    hiddenField = document.createElement('input');
                    hiddenField.type = 'hidden';
                    hiddenField.name = 'clientData';
                    hiddenField.id = 'forensicData';
                    form.appendChild(hiddenField);
                }
                hiddenField.value = JSON.stringify(essentialData);
            } else {
                // Create or update hidden field
                let hiddenField = document.getElementById('forensicData');
                if (!hiddenField) {
                    hiddenField = document.createElement('input');
                    hiddenField.type = 'hidden';
                    hiddenField.name = 'clientData';
                    hiddenField.id = 'forensicData';
                    form.appendChild(hiddenField);
                }
                hiddenField.value = dataString;
            }
        });
    }

    // Initialize when DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function() {
            collectForensicData();
            injectForensicData();
        });
    } else {
        collectForensicData();
        injectForensicData();
    }

    // Debug mode
    if (config.debugMode) {
        window.forensicData = forensicData;
        console.log('Forensic tracker initialized', forensicData);
    }

})();
// Character counter
document.addEventListener('DOMContentLoaded', function() {
  const messageField = document.getElementById('message');
  const charCount = document.getElementById('char-count');
  const submitBtn = document.getElementById('submitBtn');
  
  messageField.addEventListener('input', function() {
    const count = this.value.length;
    charCount.textContent = count;
    
    // Visual feedback for character limit
    if (count > 1800) {
      charCount.style.color = '#e74c3c';
    } else if (count > 1500) {
      charCount.style.color = '#f39c12';
    } else {
      charCount.style.color = '#27ae60';
    }
  });
  
  // Form submission feedback
  document.getElementById('contactForm').addEventListener('submit', function() {
    submitBtn.textContent = 'Sending...';
    submitBtn.disabled = true;
  });
});