// DNS Reconnaissance Tool - Enhanced Frontend JavaScript with Debug Output class ReconTool { constructor() { this.currentScanId = null; this.pollInterval = null; this.liveDataInterval = null; this.currentReport = null; this.debugMode = true; // Enable debug logging this.init(); } debug(message, data = null) { if (this.debugMode) { if (data) { console.log(`🔍 DEBUG: ${message}`, data); } else { console.log(`🔍 DEBUG: ${message}`); } } } init() { this.bindEvents(); this.setupRealtimeElements(); } setupRealtimeElements() { // Create live discovery container if it doesn't exist if (!document.getElementById('liveDiscoveries')) { const progressSection = document.getElementById('progressSection'); const liveDiv = document.createElement('div'); liveDiv.id = 'liveDiscoveries'; liveDiv.innerHTML = `
`; progressSection.appendChild(liveDiv); this.debug("Live discoveries container created"); } } bindEvents() { // Start scan button document.getElementById('startScan').addEventListener('click', () => { this.startScan(); }); // New scan button document.getElementById('newScan').addEventListener('click', () => { this.resetToForm(); }); // Report view toggles document.getElementById('showJson').addEventListener('click', () => { this.showReport('json'); }); document.getElementById('showText').addEventListener('click', () => { this.showReport('text'); }); // Download buttons document.getElementById('downloadJson').addEventListener('click', () => { this.downloadReport('json'); }); document.getElementById('downloadText').addEventListener('click', () => { this.downloadReport('text'); }); // Enter key in target field document.getElementById('target').addEventListener('keypress', (e) => { if (e.key === 'Enter') { this.startScan(); } }); } async startScan() { const target = document.getElementById('target').value.trim(); if (!target) { alert('Please enter a target domain or hostname'); return; } const scanData = { target: target, max_depth: parseInt(document.getElementById('maxDepth').value), shodan_key: document.getElementById('shodanKey').value.trim() || null, virustotal_key: document.getElementById('virustotalKey').value.trim() || null }; try { // Show progress section this.showProgressSection(); this.updateProgress(0, 'Starting scan...'); this.debug('Starting scan with data:', scanData); const response = await fetch('/api/scan', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(scanData) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); if (result.error) { throw new Error(result.error); } this.currentScanId = result.scan_id; this.debug('Scan started with ID:', this.currentScanId); this.startPolling(); this.startLiveDataPolling(); } catch (error) { console.error('❌ Failed to start scan:', error); this.showError(`Failed to start scan: ${error.message}`); } } startPolling() { this.debug('Starting status polling...'); // Poll every 2 seconds for status updates this.pollInterval = setInterval(() => { this.checkScanStatus(); }, 2000); // Also check immediately this.checkScanStatus(); } startLiveDataPolling() { this.debug('Starting live data polling...'); // Poll every 3 seconds for live data updates this.liveDataInterval = setInterval(() => { this.updateLiveData(); }, 3000); // Show the live discoveries section const liveSection = document.querySelector('.live-discoveries'); if (liveSection) { liveSection.style.display = 'block'; this.debug('Live discoveries section made visible'); } else { this.debug('ERROR: Live discoveries section not found!'); } // Also update immediately this.updateLiveData(); } async checkScanStatus() { if (!this.currentScanId) { return; } try { const response = await fetch(`/api/scan/${this.currentScanId}/status`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const status = await response.json(); if (status.error) { throw new Error(status.error); } // Update progress this.updateProgress(status.progress, status.message); // Update live stats if (status.live_stats) { this.debug('Received live stats:', status.live_stats); this.updateLiveStats(status.live_stats); } // Check if completed if (status.status === 'completed') { this.debug('Scan completed, loading report...'); this.stopPolling(); await this.loadScanReport(); } else if (status.status === 'error') { this.stopPolling(); throw new Error(status.error || 'Scan failed'); } } catch (error) { console.error('❌ Error checking scan status:', error); this.stopPolling(); this.showError(`Error checking scan status: ${error.message}`); } } async updateLiveData() { if (!this.currentScanId) { return; } this.debug(`Fetching live data for scan: ${this.currentScanId}`); try { const response = await fetch(`/api/scan/${this.currentScanId}/live-data`); if (!response.ok) { this.debug(`Live data request failed: HTTP ${response.status}`); return; // Silently fail for live data } const data = await response.json(); if (data.error) { this.debug('Live data error:', data.error); return; // Silently fail for live data } this.debug('Received live data:', data); // Update live discoveries this.updateLiveDiscoveries(data); } catch (error) { // Silently fail for live data updates this.debug('Live data update failed:', error); } } updateLiveStats(stats) { this.debug('Updating live stats:', stats); // Update the live statistics counters const statElements = { 'liveHostnames': stats.hostnames || 0, 'liveIPs': stats.ip_addresses || 0, 'liveDNS': stats.dns_records || 0, 'liveCerts': stats.certificates || 0, 'liveShodan': stats.shodan_results || 0, 'liveVT': stats.virustotal_results || 0 }; Object.entries(statElements).forEach(([elementId, value]) => { const element = document.getElementById(elementId); if (element) { const currentValue = element.textContent; element.textContent = value; if (currentValue !== value.toString()) { this.debug(`Updated ${elementId}: ${currentValue} -> ${value}`); // Add a brief highlight effect when value changes element.style.backgroundColor = '#ff9900'; setTimeout(() => { element.style.backgroundColor = ''; }, 1000); } } else { this.debug(`ERROR: Element ${elementId} not found!`); } }); } updateLiveDiscoveries(data) { this.debug('Updating live discoveries with data:', data); // Update hostnames list const hostnameList = document.querySelector('#recentHostnames .hostname-list'); if (hostnameList && data.hostnames && data.hostnames.length > 0) { // Show last 10 hostnames const recentHostnames = data.hostnames.slice(-10); hostnameList.innerHTML = recentHostnames.map(hostname => `${hostname}` ).join(''); this.debug(`Updated hostname list with ${recentHostnames.length} items`); } else if (hostnameList) { this.debug(`No hostnames to display (${data.hostnames ? data.hostnames.length : 0} total)`); } // Update IP addresses list const ipList = document.querySelector('#recentIPs .ip-list'); if (ipList && data.ip_addresses && data.ip_addresses.length > 0) { // Show last 10 IPs const recentIPs = data.ip_addresses.slice(-10); ipList.innerHTML = recentIPs.map(ip => `${ip}` ).join(''); this.debug(`Updated IP list with ${recentIPs.length} items`); } else if (ipList) { this.debug(`No IPs to display (${data.ip_addresses ? data.ip_addresses.length : 0} total)`); } // Update activity log const activityList = document.querySelector('#activityLog .activity-list'); if (activityList && data.latest_discoveries && data.latest_discoveries.length > 0) { const activities = data.latest_discoveries.slice(-5); // Last 5 activities activityList.innerHTML = activities.map(activity => { const time = new Date(activity.timestamp * 1000).toLocaleTimeString(); return `