flask app
This commit is contained in:
280
static/script.js
Normal file
280
static/script.js
Normal file
@@ -0,0 +1,280 @@
|
||||
// DNS Reconnaissance Tool - Frontend JavaScript
|
||||
|
||||
class ReconTool {
|
||||
constructor() {
|
||||
this.currentScanId = null;
|
||||
this.pollInterval = null;
|
||||
this.currentReport = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
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...');
|
||||
|
||||
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.startPolling();
|
||||
|
||||
} catch (error) {
|
||||
this.showError(`Failed to start scan: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
startPolling() {
|
||||
// Poll every 2 seconds for updates
|
||||
this.pollInterval = setInterval(() => {
|
||||
this.checkScanStatus();
|
||||
}, 2000);
|
||||
|
||||
// Also check immediately
|
||||
this.checkScanStatus();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Check if completed
|
||||
if (status.status === 'completed') {
|
||||
this.stopPolling();
|
||||
await this.loadScanReport();
|
||||
} else if (status.status === 'error') {
|
||||
this.stopPolling();
|
||||
throw new Error(status.error || 'Scan failed');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this.stopPolling();
|
||||
this.showError(`Error checking scan status: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async loadScanReport() {
|
||||
try {
|
||||
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.showResultsSection();
|
||||
this.showReport('text'); // Default to text view
|
||||
|
||||
} catch (error) {
|
||||
this.showError(`Error loading report: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
stopPolling() {
|
||||
if (this.pollInterval) {
|
||||
clearInterval(this.pollInterval);
|
||||
this.pollInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
showProgressSection() {
|
||||
document.getElementById('scanForm').style.display = 'none';
|
||||
document.getElementById('progressSection').style.display = 'block';
|
||||
document.getElementById('resultsSection').style.display = 'none';
|
||||
}
|
||||
|
||||
showResultsSection() {
|
||||
document.getElementById('scanForm').style.display = 'none';
|
||||
document.getElementById('progressSection').style.display = 'none';
|
||||
document.getElementById('resultsSection').style.display = 'block';
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
// Clear form
|
||||
document.getElementById('target').value = '';
|
||||
document.getElementById('shodanKey').value = '';
|
||||
document.getElementById('virustotalKey').value = '';
|
||||
document.getElementById('maxDepth').value = '2';
|
||||
}
|
||||
|
||||
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 {
|
||||
const jsonData = JSON.parse(this.currentReport.json_report);
|
||||
reportContent.textContent = JSON.stringify(jsonData, null, 2);
|
||||
} catch (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 = this.currentReport.json_report;
|
||||
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', () => {
|
||||
new ReconTool();
|
||||
});
|
||||
258
static/style.css
Normal file
258
static/style.css
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
███████╗██████╗ ███████╗ ██████╗████████╗ ██████╗ ██████╗ ██╗ ██╗███████╗
|
||||
██╔════╝██╔══██╗██╔════╝██╔═══██╗╚══██╔══╝ ██╔═══██╗██╔═══██╗╚██╗██╔╝██╔════╝
|
||||
███████╗██████╔╝█████╗ ██║ ██║ ██║ ██║ ██║██║ ██║ ╚███╔╝ ███████╗
|
||||
╚════██║██╔══██╗██╔══╝ ██║ ██║ ██║ ██║ ██║██║ ██║ ██╔██╗ ╚════██║
|
||||
███████║██║ ██║███████╗╚██████╔╝ ██║ ╚██████╔╝╚██████╔╝██╔╝ ██╗███████║
|
||||
╚══════╝╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
|
||||
|
||||
TACTICAL THEME - DNS RECONNAISSANCE INTERFACE
|
||||
STYLE OVERRIDE
|
||||
*/
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Roboto Mono', 'Lucida Console', Monaco, monospace;
|
||||
line-height: 1.6;
|
||||
color: #c7c7c7; /* Light grey for readability */
|
||||
/* Dark, textured background for a gritty feel */
|
||||
background-color: #1a1a1a;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3E%3Cpath fill='%23333333' fill-opacity='0.4' d='M1 3h1v1H1V3zm2-2h1v1H3V1z'%3E%3C/path%3E%3C/svg%3E");
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
color: #e0e0e0;
|
||||
margin-bottom: 40px;
|
||||
border-bottom: 1px solid #444;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-family: 'Special Elite', 'Courier New', monospace; /* Stencil / Typewriter font */
|
||||
font-size: 2.8rem;
|
||||
color: #00ff41; /* Night-vision green */
|
||||
text-shadow: 0 0 5px rgba(0, 255, 65, 0.5);
|
||||
margin-bottom: 10px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
header p {
|
||||
font-size: 1.1rem;
|
||||
color: #a0a0a0;
|
||||
}
|
||||
|
||||
.scan-form, .progress-section, .results-section {
|
||||
background: #2a2a2a; /* Dark charcoal */
|
||||
border-radius: 4px; /* Sharper edges */
|
||||
border: 1px solid #444;
|
||||
box-shadow: inset 0 0 15px rgba(0,0,0,0.5);
|
||||
padding: 30px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.scan-form h2, .progress-section h2, .results-section h2 {
|
||||
margin-bottom: 20px;
|
||||
color: #e0e0e0;
|
||||
border-bottom: 1px solid #555;
|
||||
padding-bottom: 10px;
|
||||
text-transform: uppercase; /* Military style */
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #b0b0b0;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.form-group input, .form-group select {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: #1a1a1a;
|
||||
border: 1px solid #555;
|
||||
border-radius: 2px;
|
||||
font-size: 16px;
|
||||
color: #00ff41; /* Green text for input fields */
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.form-group input:focus, .form-group select:focus {
|
||||
outline: none;
|
||||
border-color: #ff9900; /* Amber focus color */
|
||||
box-shadow: 0 0 5px rgba(255, 153, 0, 0.5);
|
||||
}
|
||||
|
||||
.api-keys {
|
||||
background: rgba(0,0,0,0.3);
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #444;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.api-keys h3 {
|
||||
margin-bottom: 15px;
|
||||
color: #c7c7c7;
|
||||
}
|
||||
|
||||
.btn-primary, .btn-secondary {
|
||||
padding: 12px 24px;
|
||||
border: 1px solid #666;
|
||||
border-radius: 2px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #2c5c34; /* Dark military green */
|
||||
color: #e0e0e0;
|
||||
border-color: #3b7b46;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #3b7b46; /* Lighter green on hover */
|
||||
color: #fff;
|
||||
border-color: #4cae5c;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #4a4a4a; /* Dark grey */
|
||||
color: #c7c7c7;
|
||||
border-color: #666;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #5a5a5a;
|
||||
}
|
||||
|
||||
.btn-secondary.active {
|
||||
background: #6a4f2a; /* Amber/Brown for active state */
|
||||
color: #fff;
|
||||
border-color: #ff9900;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
background: #1a1a1a;
|
||||
border: 1px solid #555;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 15px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: #ff9900; /* Solid amber progress fill */
|
||||
width: 0%;
|
||||
transition: width 0.3s ease;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#progressMessage {
|
||||
font-weight: 500;
|
||||
color: #a0a0a0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.scan-controls {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.results-controls {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.report-container {
|
||||
background: #0a0a0a; /* Near-black terminal background */
|
||||
border-radius: 4px;
|
||||
border: 1px solid #333;
|
||||
padding: 20px;
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
box-shadow: inset 0 0 10px #000;
|
||||
}
|
||||
|
||||
#reportContent {
|
||||
color: #00ff41; /* Classic terminal green */
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Responsive design adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.scan-form, .progress-section, .results-section {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.btn-primary, .btn-secondary {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.results-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.results-controls button {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tactical loading spinner */
|
||||
.loading {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid rgba(199, 199, 199, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: #00ff41; /* Night-vision green spinner */
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
Reference in New Issue
Block a user