555 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			555 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// 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 = `
 | 
						|
                <div class="live-discoveries" style="display: none;">
 | 
						|
                    <h3>🔍 Live Discoveries</h3>
 | 
						|
                    <div class="stats-grid">
 | 
						|
                        <div class="stat-item">
 | 
						|
                            <span class="stat-label">Hostnames:</span>
 | 
						|
                            <span id="liveHostnames" class="stat-value">0</span>
 | 
						|
                        </div>
 | 
						|
                        <div class="stat-item">
 | 
						|
                            <span class="stat-label">IP Addresses:</span>
 | 
						|
                            <span id="liveIPs" class="stat-value">0</span>
 | 
						|
                        </div>
 | 
						|
                        <div class="stat-item">
 | 
						|
                            <span class="stat-label">DNS Records:</span>
 | 
						|
                            <span id="liveDNS" class="stat-value">0</span>
 | 
						|
                        </div>
 | 
						|
                        <div class="stat-item">
 | 
						|
                            <span class="stat-label">Certificates:</span>
 | 
						|
                            <span id="liveCerts" class="stat-value">0</span>
 | 
						|
                        </div>
 | 
						|
                        <div class="stat-item">
 | 
						|
                            <span class="stat-label">Shodan Results:</span>
 | 
						|
                            <span id="liveShodan" class="stat-value">0</span>
 | 
						|
                        </div>
 | 
						|
                        <div class="stat-item">
 | 
						|
                            <span class="stat-label">VirusTotal:</span>
 | 
						|
                            <span id="liveVT" class="stat-value">0</span>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                    <div class="discoveries-list">
 | 
						|
                        <h4>📋 Recent Discoveries</h4>
 | 
						|
                        <div id="recentHostnames" class="discovery-section">
 | 
						|
                            <strong>Hostnames:</strong>
 | 
						|
                            <div class="hostname-list"></div>
 | 
						|
                        </div>
 | 
						|
                        <div id="recentIPs" class="discovery-section">
 | 
						|
                            <strong>IP Addresses:</strong>
 | 
						|
                            <div class="ip-list"></div>
 | 
						|
                        </div>
 | 
						|
                        <div id="activityLog" class="discovery-section">
 | 
						|
                            <strong>Activity Log:</strong>
 | 
						|
                            <div class="activity-list"></div>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
            `;
 | 
						|
            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 => 
 | 
						|
                `<span class="discovery-item">${hostname}</span>`
 | 
						|
            ).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 => 
 | 
						|
                `<span class="discovery-item">${ip}</span>`
 | 
						|
            ).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 `<div class="activity-item">[${time}] ${activity.message}</div>`;
 | 
						|
            }).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();
 | 
						|
}); |