progress
This commit is contained in:
210
static/script.js
210
static/script.js
@@ -1,15 +1,73 @@
|
||||
// DNS Reconnaissance Tool - Frontend JavaScript
|
||||
// DNS Reconnaissance Tool - Enhanced Frontend JavaScript with Real-time Updates
|
||||
|
||||
class ReconTool {
|
||||
constructor() {
|
||||
this.currentScanId = null;
|
||||
this.pollInterval = null;
|
||||
this.liveDataInterval = null;
|
||||
this.currentReport = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
@@ -69,6 +127,8 @@ class ReconTool {
|
||||
this.showProgressSection();
|
||||
this.updateProgress(0, 'Starting scan...');
|
||||
|
||||
console.log('🚀 Starting scan with data:', scanData);
|
||||
|
||||
const response = await fetch('/api/scan', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -88,15 +148,19 @@ class ReconTool {
|
||||
}
|
||||
|
||||
this.currentScanId = result.scan_id;
|
||||
console.log('✅ 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() {
|
||||
// Poll every 2 seconds for updates
|
||||
// Poll every 2 seconds for status updates
|
||||
this.pollInterval = setInterval(() => {
|
||||
this.checkScanStatus();
|
||||
}, 2000);
|
||||
@@ -104,6 +168,22 @@ class ReconTool {
|
||||
// Also check immediately
|
||||
this.checkScanStatus();
|
||||
}
|
||||
|
||||
startLiveDataPolling() {
|
||||
// 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';
|
||||
}
|
||||
|
||||
// Also update immediately
|
||||
this.updateLiveData();
|
||||
}
|
||||
|
||||
async checkScanStatus() {
|
||||
if (!this.currentScanId) {
|
||||
@@ -125,9 +205,15 @@ class ReconTool {
|
||||
|
||||
// Update progress
|
||||
this.updateProgress(status.progress, status.message);
|
||||
|
||||
// Update live stats
|
||||
if (status.live_stats) {
|
||||
this.updateLiveStats(status.live_stats);
|
||||
}
|
||||
|
||||
// Check if completed
|
||||
if (status.status === 'completed') {
|
||||
console.log('✅ Scan completed');
|
||||
this.stopPolling();
|
||||
await this.loadScanReport();
|
||||
} else if (status.status === 'error') {
|
||||
@@ -136,13 +222,101 @@ class ReconTool {
|
||||
}
|
||||
|
||||
} 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;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/scan/${this.currentScanId}/live-data`);
|
||||
|
||||
if (!response.ok) {
|
||||
return; // Silently fail for live data
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
return; // Silently fail for live data
|
||||
}
|
||||
|
||||
// Update live discoveries
|
||||
this.updateLiveDiscoveries(data);
|
||||
|
||||
} catch (error) {
|
||||
// Silently fail for live data updates
|
||||
console.debug('Live data update failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
updateLiveStats(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) {
|
||||
element.textContent = value;
|
||||
|
||||
// Add a brief highlight effect when value changes
|
||||
if (element.textContent !== value.toString()) {
|
||||
element.style.backgroundColor = '#ff9900';
|
||||
setTimeout(() => {
|
||||
element.style.backgroundColor = '';
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateLiveDiscoveries(data) {
|
||||
// Update hostnames list
|
||||
const hostnameList = document.querySelector('#recentHostnames .hostname-list');
|
||||
if (hostnameList && data.hostnames) {
|
||||
// Show last 10 hostnames
|
||||
const recentHostnames = data.hostnames.slice(-10);
|
||||
hostnameList.innerHTML = recentHostnames.map(hostname =>
|
||||
`<span class="discovery-item">${hostname}</span>`
|
||||
).join('');
|
||||
}
|
||||
|
||||
// Update IP addresses list
|
||||
const ipList = document.querySelector('#recentIPs .ip-list');
|
||||
if (ipList && data.ip_addresses) {
|
||||
// Show last 10 IPs
|
||||
const recentIPs = data.ip_addresses.slice(-10);
|
||||
ipList.innerHTML = recentIPs.map(ip =>
|
||||
`<span class="discovery-item">${ip}</span>`
|
||||
).join('');
|
||||
}
|
||||
|
||||
// Update activity log
|
||||
const activityList = document.querySelector('#activityLog .activity-list');
|
||||
if (activityList && data.latest_discoveries) {
|
||||
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('');
|
||||
}
|
||||
}
|
||||
|
||||
async loadScanReport() {
|
||||
try {
|
||||
console.log('📄 Loading scan report...');
|
||||
const response = await fetch(`/api/scan/${this.currentScanId}/report`);
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -156,10 +330,12 @@ class ReconTool {
|
||||
}
|
||||
|
||||
this.currentReport = report;
|
||||
console.log('✅ 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}`);
|
||||
}
|
||||
}
|
||||
@@ -169,6 +345,10 @@ class ReconTool {
|
||||
clearInterval(this.pollInterval);
|
||||
this.pollInterval = null;
|
||||
}
|
||||
if (this.liveDataInterval) {
|
||||
clearInterval(this.liveDataInterval);
|
||||
this.liveDataInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
showProgressSection() {
|
||||
@@ -181,6 +361,12 @@ class ReconTool {
|
||||
document.getElementById('scanForm').style.display = 'none';
|
||||
document.getElementById('progressSection').style.display = 'none';
|
||||
document.getElementById('resultsSection').style.display = 'block';
|
||||
|
||||
// Hide live discoveries in results section
|
||||
const liveSection = document.querySelector('.live-discoveries');
|
||||
if (liveSection) {
|
||||
liveSection.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
resetToForm() {
|
||||
@@ -192,6 +378,12 @@ class ReconTool {
|
||||
document.getElementById('progressSection').style.display = 'none';
|
||||
document.getElementById('resultsSection').style.display = 'none';
|
||||
|
||||
// Hide live discoveries
|
||||
const liveSection = document.querySelector('.live-discoveries');
|
||||
if (liveSection) {
|
||||
liveSection.style.display = 'none';
|
||||
}
|
||||
|
||||
// Clear form
|
||||
document.getElementById('target').value = '';
|
||||
document.getElementById('shodanKey').value = '';
|
||||
@@ -227,9 +419,16 @@ class ReconTool {
|
||||
if (type === 'json') {
|
||||
// Show JSON report
|
||||
try {
|
||||
const jsonData = JSON.parse(this.currentReport.json_report);
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -252,7 +451,9 @@ class ReconTool {
|
||||
let content, filename, mimeType;
|
||||
|
||||
if (type === 'json') {
|
||||
content = this.currentReport.json_report;
|
||||
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 {
|
||||
@@ -276,5 +477,6 @@ class ReconTool {
|
||||
|
||||
// Initialize the application when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('🌐 DNS Reconnaissance Tool initialized');
|
||||
new ReconTool();
|
||||
});
|
||||
124
static/style.css
124
static/style.css
@@ -211,6 +211,40 @@ header p {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
|
||||
.hostname-list, .ip-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.discovery-item {
|
||||
background: #2a2a2a;
|
||||
color: #00ff41;
|
||||
padding: 2px 6px;
|
||||
border-radius: 2px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.8rem;
|
||||
border: 1px solid #444;
|
||||
}
|
||||
|
||||
.activity-list {
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.activity-item {
|
||||
color: #a0a0a0;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.8rem;
|
||||
padding: 2px 0;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.activity-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Responsive design adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
@@ -240,6 +274,23 @@ header p {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
}
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.stat-label, .stat-value {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.hostname-list, .ip-list {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tactical loading spinner */
|
||||
@@ -253,6 +304,79 @@ header p {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.live-discoveries {
|
||||
background: rgba(0, 20, 0, 0.6);
|
||||
border: 1px solid #00ff41;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.live-discoveries h3 {
|
||||
color: #00ff41;
|
||||
margin-bottom: 15px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid #333;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #a0a0a0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
color: #00ff41;
|
||||
font-weight: bold;
|
||||
font-family: 'Courier New', monospace;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.discoveries-list {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.discoveries-list h4 {
|
||||
color: #ff9900;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 1px solid #444;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.discovery-section {
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid #333;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.discovery-section strong {
|
||||
color: #c7c7c7;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
Reference in New Issue
Block a user