This commit is contained in:
overcuriousity 2025-09-14 14:28:04 +02:00
parent 2185177a84
commit c91913fa13
3 changed files with 42 additions and 86 deletions

102
app.py
View File

@ -19,43 +19,30 @@ app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=2) # 2 hour session
def get_user_scanner(): def get_user_scanner():
""" """
User scanner retrieval with better error handling and debugging. Retrieves the scanner for the current session, or creates a new
session and scanner if one doesn't exist.
""" """
# Get current Flask session info for debugging # Get current Flask session info for debugging
current_flask_session_id = session.get('dnsrecon_session_id') current_flask_session_id = session.get('dnsrecon_session_id')
client_ip = request.remote_addr
user_agent = request.headers.get('User-Agent', '')[:100] # Truncate for logging
# Try to get existing session # Try to get existing session
if current_flask_session_id: if current_flask_session_id:
existing_scanner = session_manager.get_session(current_flask_session_id) existing_scanner = session_manager.get_session(current_flask_session_id)
if existing_scanner: if existing_scanner:
# Ensure session ID is set
existing_scanner.session_id = current_flask_session_id
return current_flask_session_id, existing_scanner return current_flask_session_id, existing_scanner
else:
print(f"Session {current_flask_session_id} not found in session manager")
# Create new session # Create new session if none exists
print("Creating new session...") print("Creating new session as none was found...")
new_session_id = session_manager.create_session() new_session_id = session_manager.create_session()
new_scanner = session_manager.get_session(new_session_id) new_scanner = session_manager.get_session(new_session_id)
if not new_scanner: if not new_scanner:
print(f"ERROR: Failed to retrieve newly created session {new_session_id}")
raise Exception("Failed to create new scanner session") raise Exception("Failed to create new scanner session")
# Store in Flask session # Store in Flask session
session['dnsrecon_session_id'] = new_session_id session['dnsrecon_session_id'] = new_session_id
session.permanent = True session.permanent = True
# Ensure session ID is set on scanner
new_scanner.session_id = new_session_id
print(f"Created new session: {new_session_id}")
print(f"New scanner status: {new_scanner.status}")
print("=== END SESSION DEBUG ===")
return new_session_id, new_scanner return new_session_id, new_scanner
@app.route('/') @app.route('/')
@ -67,101 +54,68 @@ def index():
@app.route('/api/scan/start', methods=['POST']) @app.route('/api/scan/start', methods=['POST'])
def start_scan(): def start_scan():
""" """
Start a new reconnaissance scan with immediate GUI feedback. Start a new reconnaissance scan. Creates a new isolated scanner if
clear_graph is true, otherwise adds to the existing one.
""" """
print("=== API: /api/scan/start called ===") print("=== API: /api/scan/start called ===")
try: try:
print("Getting JSON data from request...")
data = request.get_json() data = request.get_json()
print(f"Request data: {data}")
if not data or 'target_domain' not in data: if not data or 'target_domain' not in data:
print("ERROR: Missing target_domain in request") return jsonify({'success': False, 'error': 'Missing target_domain in request'}), 400
return jsonify({
'success': False,
'error': 'Missing target_domain in request'
}), 400
target_domain = data['target_domain'].strip() target_domain = data['target_domain'].strip()
max_depth = data.get('max_depth', config.default_recursion_depth) max_depth = data.get('max_depth', config.default_recursion_depth)
clear_graph = data.get('clear_graph', True) clear_graph = data.get('clear_graph', True)
print(f"Parsed - target_domain: '{target_domain}', max_depth: {max_depth}") print(f"Parsed - target_domain: '{target_domain}', max_depth: {max_depth}, clear_graph: {clear_graph}")
# Validation # Validation
if not target_domain: if not target_domain:
print("ERROR: Target domain cannot be empty") return jsonify({'success': False, 'error': 'Target domain cannot be empty'}), 400
return jsonify({ if not isinstance(max_depth, int) or not 1 <= max_depth <= 5:
'success': False, return jsonify({'success': False, 'error': 'Max depth must be an integer between 1 and 5'}), 400
'error': 'Target domain cannot be empty'
}), 400
if not isinstance(max_depth, int) or max_depth < 1 or max_depth > 5: user_session_id, scanner = None, None
print(f"ERROR: Invalid max_depth: {max_depth}")
return jsonify({
'success': False,
'error': 'Max depth must be an integer between 1 and 5'
}), 400
print("Validation passed, getting user scanner...") if clear_graph:
print("Clear graph requested: Creating a new, isolated scanner session.")
old_session_id = session.get('dnsrecon_session_id')
if old_session_id:
session_manager.terminate_session(old_session_id)
# Get user-specific scanner user_session_id = session_manager.create_session()
session['dnsrecon_session_id'] = user_session_id
session.permanent = True
scanner = session_manager.get_session(user_session_id)
else:
print("Adding to existing graph: Reusing the current scanner session.")
user_session_id, scanner = get_user_scanner() user_session_id, scanner = get_user_scanner()
# Ensure session ID is properly set if not scanner:
if not scanner.session_id: return jsonify({'success': False, 'error': 'Failed to get or create a scanner instance.'}), 500
scanner.session_id = user_session_id
print(f"Using session: {user_session_id}") print(f"Using scanner {id(scanner)} in session {user_session_id}")
print(f"Scanner object ID: {id(scanner)}")
# Start scan
print(f"Calling start_scan on scanner {id(scanner)}...")
success = scanner.start_scan(target_domain, max_depth, clear_graph=clear_graph) success = scanner.start_scan(target_domain, max_depth, clear_graph=clear_graph)
# Immediately update session state regardless of success
session_manager.update_session_scanner(user_session_id, scanner)
if success: if success:
scan_session_id = scanner.logger.session_id
print(f"Scan started successfully with scan session ID: {scan_session_id}")
return jsonify({ return jsonify({
'success': True, 'success': True,
'message': 'Scan started successfully', 'message': 'Scan started successfully',
'scan_id': scan_session_id, 'scan_id': scanner.logger.session_id,
'user_session_id': user_session_id, 'user_session_id': user_session_id,
'scanner_status': scanner.status,
'debug_info': {
'scanner_object_id': id(scanner),
'scanner_status': scanner.status
}
}) })
else: else:
print("ERROR: Scanner returned False")
# Provide more detailed error information
error_details = {
'scanner_status': scanner.status,
'scanner_object_id': id(scanner),
'session_id': user_session_id,
'providers_count': len(scanner.providers) if hasattr(scanner, 'providers') else 0
}
return jsonify({ return jsonify({
'success': False, 'success': False,
'error': f'Failed to start scan (scanner status: {scanner.status})', 'error': f'Failed to start scan (scanner status: {scanner.status})',
'debug_info': error_details
}), 409 }), 409
except Exception as e: except Exception as e:
print(f"ERROR: Exception in start_scan endpoint: {e}") print(f"ERROR: Exception in start_scan endpoint: {e}")
traceback.print_exc() traceback.print_exc()
return jsonify({ return jsonify({'success': False, 'error': f'Internal server error: {str(e)}'}), 500
'success': False,
'error': f'Internal server error: {str(e)}'
}), 500
@app.route('/api/scan/stop', methods=['POST']) @app.route('/api/scan/stop', methods=['POST'])
def stop_scan(): def stop_scan():

View File

@ -198,6 +198,7 @@ class Scanner:
if self.scan_thread and self.scan_thread.is_alive(): if self.scan_thread and self.scan_thread.is_alive():
print("A previous scan thread is still alive. Sending termination signal and waiting...") print("A previous scan thread is still alive. Sending termination signal and waiting...")
self.stop_scan() self.stop_scan()
# Wait for the thread to die, with a timeout
self.scan_thread.join(10.0) self.scan_thread.join(10.0)
if self.scan_thread.is_alive(): if self.scan_thread.is_alive():
@ -209,7 +210,8 @@ class Scanner:
# Reset state for new scan # Reset state for new scan
self.status = ScanStatus.IDLE self.status = ScanStatus.IDLE
self._update_session_state() # Update GUI immediately # This update is crucial for the UI to reflect the reset before the new scan starts.
self._update_session_state()
print("Scanner state is now clean for a new scan.") print("Scanner state is now clean for a new scan.")
try: try:
@ -225,7 +227,7 @@ class Scanner:
self.max_depth = max_depth self.max_depth = max_depth
self.current_depth = 0 self.current_depth = 0
# Clear both local and Redis stop signals # Clear both local and Redis stop signals for the new scan
self.stop_event.clear() self.stop_event.clear()
if self.session_id: if self.session_id:
from core.session_manager import session_manager from core.session_manager import session_manager
@ -235,14 +237,14 @@ class Scanner:
self.indicators_processed = 0 self.indicators_processed = 0
self.current_indicator = self.current_target self.current_indicator = self.current_target
# Update GUI with scan preparation # Update GUI with scan preparation state
self._update_session_state() self._update_session_state()
# Start new forensic session # Start new forensic session
print(f"Starting new forensic session for scanner {id(self)}...") print(f"Starting new forensic session for scanner {id(self)}...")
self.logger = new_session() self.logger = new_session()
# Start scan in separate thread # Start scan in a separate thread
print(f"Starting scan thread for scanner {id(self)}...") print(f"Starting scan thread for scanner {id(self)}...")
self.scan_thread = threading.Thread( self.scan_thread = threading.Thread(
target=self._execute_scan, target=self._execute_scan,
@ -258,7 +260,8 @@ class Scanner:
print(f"ERROR: Exception in start_scan for scanner {id(self)}: {e}") print(f"ERROR: Exception in start_scan for scanner {id(self)}: {e}")
traceback.print_exc() traceback.print_exc()
self.status = ScanStatus.FAILED self.status = ScanStatus.FAILED
self._update_session_state() # Update failed status immediately # Update GUI with failed status immediately
self._update_session_state()
return False return False
def _execute_scan(self, target_domain: str, max_depth: int) -> None: def _execute_scan(self, target_domain: str, max_depth: int) -> None:
@ -648,7 +651,7 @@ class Scanner:
# Immediately update GUI with stopped status # Immediately update GUI with stopped status
self._update_session_state() self._update_session_state()
# Cancel executor futures if running # Aggressively shut down the executor and cancel all pending tasks
if self.executor: if self.executor:
print("Shutting down executor with immediate cancellation...") print("Shutting down executor with immediate cancellation...")
self.executor.shutdown(wait=False, cancel_futures=True) self.executor.shutdown(wait=False, cancel_futures=True)

View File

@ -348,7 +348,6 @@ class BaseProvider(ABC):
return True return True
return False return False
def _wait_with_cancellation_check(self) -> bool: def _wait_with_cancellation_check(self) -> bool:
""" """
Wait for rate limiting while aggressively checking for cancellation. Wait for rate limiting while aggressively checking for cancellation.