flask app

This commit is contained in:
overcuriousity
2025-09-09 13:55:05 +02:00
parent 941d815595
commit 8263f5cfa9
18 changed files with 3461 additions and 1 deletions

280
static/script.js Normal file
View 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
View 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); }
}