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;
|
|
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;
|
|
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();
|
|
}); |