# File: src/web_app.py """Flask web application for reconnaissance tool.""" from flask import Flask, render_template, request, jsonify, send_from_directory import threading import time from .config import Config from .reconnaissance import ReconnaissanceEngine from .report_generator import ReportGenerator # Global variables for tracking ongoing scans active_scans = {} scan_lock = threading.Lock() def create_app(config: Config): """Create Flask application.""" app = Flask(__name__, template_folder='../templates', static_folder='../static') app.config['SECRET_KEY'] = 'recon-tool-secret-key' @app.route('/') def index(): """Main page.""" return render_template('index.html') @app.route('/api/scan', methods=['POST']) def start_scan(): """Start a new reconnaissance scan.""" data = request.get_json() target = data.get('target') scan_config = Config.from_args( shodan_key=data.get('shodan_key'), virustotal_key=data.get('virustotal_key'), max_depth=data.get('max_depth', 2) ) if not target: return jsonify({'error': 'Target is required'}), 400 # Generate scan ID scan_id = f"{target}_{int(time.time())}" # Initialize scan data with scan_lock: active_scans[scan_id] = { 'status': 'starting', 'progress': 0, 'message': 'Initializing...', 'data': None, 'error': None } # Start reconnaissance in background thread thread = threading.Thread( target=run_reconnaissance_background, args=(scan_id, target, scan_config) ) thread.daemon = True thread.start() return jsonify({'scan_id': scan_id}) @app.route('/api/scan//status') def get_scan_status(scan_id): """Get scan status and progress.""" with scan_lock: if scan_id not in active_scans: return jsonify({'error': 'Scan not found'}), 404 scan_data = active_scans[scan_id].copy() # Convert ReconData object to a dict to make it JSON serializable if scan_data.get('data'): scan_data['data'] = scan_data['data'].to_dict() return jsonify(scan_data) @app.route('/api/scan//report') def get_scan_report(scan_id): """Get scan report.""" with scan_lock: if scan_id not in active_scans: return jsonify({'error': 'Scan not found'}), 404 scan_data = active_scans[scan_id] if scan_data['status'] != 'completed' or not scan_data['data']: return jsonify({'error': 'Scan not completed'}), 400 # Generate report report_gen = ReportGenerator(scan_data['data']) return jsonify({ 'json_report': scan_data['data'].to_dict(), # Use to_dict for a clean JSON object 'text_report': report_gen.generate_text_report() }) return app def run_reconnaissance_background(scan_id: str, target: str, config: Config): """Run reconnaissance in background thread.""" def update_progress(message: str, percentage: int = None): """Update scan progress.""" with scan_lock: if scan_id in active_scans: active_scans[scan_id]['message'] = message if percentage is not None: active_scans[scan_id]['progress'] = percentage try: # Initialize engine engine = ReconnaissanceEngine(config) engine.set_progress_callback(update_progress) # Update status with scan_lock: active_scans[scan_id]['status'] = 'running' # Run reconnaissance data = engine.run_reconnaissance(target) # Update with results with scan_lock: active_scans[scan_id]['status'] = 'completed' active_scans[scan_id]['progress'] = 100 active_scans[scan_id]['message'] = 'Reconnaissance completed' active_scans[scan_id]['data'] = data except Exception as e: # Handle errors with scan_lock: active_scans[scan_id]['status'] = 'error' active_scans[scan_id]['error'] = str(e) active_scans[scan_id]['message'] = f'Error: {str(e)}'