// 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 `
[${time}] ${activity.message}
`; }).join(''); this.debug(`Updated activity log with ${activities.length} items`); } else if (activityList) { this.debug(`No activities to display (${data.latest_discoveries ? data.latest_discoveries.length : 0} total)`); } } async loadScanReport() { try { this.debug('Loading scan report...'); const response = await fetch(`/api/scan/${this.currentScanId}/report`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const report = await response.json(); if (report.error) { throw new Error(report.error); } this.currentReport = report; this.debug('Report loaded successfully'); this.showResultsSection(); this.showReport('text'); // Default to text view } catch (error) { console.error('❌ Error loading report:', error); this.showError(`Error loading report: ${error.message}`); } } stopPolling() { this.debug('Stopping polling intervals...'); if (this.pollInterval) { clearInterval(this.pollInterval); this.pollInterval = null; } if (this.liveDataInterval) { clearInterval(this.liveDataInterval); this.liveDataInterval = null; } } showProgressSection() { document.getElementById('scanForm').style.display = 'none'; document.getElementById('progressSection').style.display = 'block'; document.getElementById('resultsSection').style.display = 'none'; this.debug('Showing progress section'); } showResultsSection() { document.getElementById('scanForm').style.display = 'none'; document.getElementById('progressSection').style.display = 'block'; // Keep visible document.getElementById('resultsSection').style.display = 'block'; // Change the title to show it's the final summary const liveSection = document.querySelector('.live-discoveries'); if (liveSection) { const title = liveSection.querySelector('h3'); if (title) { title.textContent = '📊 Final Discovery Summary'; } liveSection.style.display = 'block'; } // Hide just the progress bar and scan controls const progressBar = document.querySelector('.progress-bar'); const progressMessage = document.getElementById('progressMessage'); const scanControls = document.querySelector('.scan-controls'); if (progressBar) progressBar.style.display = 'none'; if (progressMessage) progressMessage.style.display = 'none'; if (scanControls) scanControls.style.display = 'none'; this.debug('Showing results section with live discoveries'); } resetToForm() { this.stopPolling(); this.currentScanId = null; this.currentReport = null; document.getElementById('scanForm').style.display = 'block'; document.getElementById('progressSection').style.display = 'none'; document.getElementById('resultsSection').style.display = 'none'; // Show progress elements again const progressBar = document.querySelector('.progress-bar'); const progressMessage = document.getElementById('progressMessage'); const scanControls = document.querySelector('.scan-controls'); if (progressBar) progressBar.style.display = 'block'; if (progressMessage) progressMessage.style.display = 'block'; if (scanControls) scanControls.style.display = 'block'; // Hide live discoveries and reset title const liveSection = document.querySelector('.live-discoveries'); if (liveSection) { liveSection.style.display = 'none'; const title = liveSection.querySelector('h3'); if (title) { title.textContent = '🔍 Live Discoveries'; } } // Clear form document.getElementById('target').value = ''; document.getElementById('shodanKey').value = ''; document.getElementById('virustotalKey').value = ''; document.getElementById('maxDepth').value = '2'; this.debug('Reset to form view'); } updateProgress(percentage, message) { const progressFill = document.getElementById('progressFill'); const progressMessage = document.getElementById('progressMessage'); progressFill.style.width = `${percentage || 0}%`; progressMessage.textContent = message || 'Processing...'; } showError(message) { // Update progress section to show error this.updateProgress(0, `Error: ${message}`); // Also alert the user alert(`Error: ${message}`); } showReport(type) { if (!this.currentReport) { return; } const reportContent = document.getElementById('reportContent'); const showJsonBtn = document.getElementById('showJson'); const showTextBtn = document.getElementById('showText'); if (type === 'json') { // Show JSON report try { // The json_report should already be a string from the server let jsonData; if (typeof this.currentReport.json_report === 'string') { jsonData = JSON.parse(this.currentReport.json_report); } else { jsonData = this.currentReport.json_report; } reportContent.textContent = JSON.stringify(jsonData, null, 2); } catch (e) { console.error('Error parsing JSON report:', e); reportContent.textContent = this.currentReport.json_report; } showJsonBtn.classList.add('active'); showTextBtn.classList.remove('active'); } else { // Show text report reportContent.textContent = this.currentReport.text_report; showTextBtn.classList.add('active'); showJsonBtn.classList.remove('active'); } } downloadReport(type) { if (!this.currentReport) { return; } let content, filename, mimeType; if (type === 'json') { content = typeof this.currentReport.json_report === 'string' ? this.currentReport.json_report : JSON.stringify(this.currentReport.json_report, null, 2); filename = `recon-report-${this.currentScanId}.json`; mimeType = 'application/json'; } else { content = this.currentReport.text_report; filename = `recon-report-${this.currentScanId}.txt`; mimeType = 'text/plain'; } // Create download link const blob = new Blob([content], { type: mimeType }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); } } // Initialize the application when DOM is loaded document.addEventListener('DOMContentLoaded', () => { console.log('🌐 DNS Reconnaissance Tool initialized with debug mode'); new ReconTool(); });