it
This commit is contained in:
		
							parent
							
								
									b47e679992
								
							
						
					
					
						commit
						646b569ced
					
				
							
								
								
									
										16
									
								
								app.py
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								app.py
									
									
									
									
									
								
							@ -115,27 +115,11 @@ def start_scan():
 | 
			
		||||
        user_session_id, scanner = get_user_scanner()
 | 
			
		||||
        print(f"Using session: {user_session_id}")
 | 
			
		||||
        print(f"Scanner object ID: {id(scanner)}")
 | 
			
		||||
        print(f"Scanner status before start: {scanner.status}")
 | 
			
		||||
        
 | 
			
		||||
        # Additional safety check - if scanner is somehow in running state, force reset
 | 
			
		||||
        if scanner.status == 'running':
 | 
			
		||||
            print(f"WARNING: Scanner in session {user_session_id} was already running - forcing reset")
 | 
			
		||||
            scanner.stop_scan()
 | 
			
		||||
            # Give it a moment to stop
 | 
			
		||||
            import time
 | 
			
		||||
            time.sleep(1)
 | 
			
		||||
            
 | 
			
		||||
            # If still running, force status reset
 | 
			
		||||
            if scanner.status == 'running':
 | 
			
		||||
                print("WARNING: Force resetting scanner status from 'running' to 'idle'")
 | 
			
		||||
                scanner.status = 'idle'
 | 
			
		||||
        
 | 
			
		||||
        # Start scan
 | 
			
		||||
        print(f"Calling start_scan on scanner {id(scanner)}...")
 | 
			
		||||
        success = scanner.start_scan(target_domain, max_depth, clear_graph=clear_graph)
 | 
			
		||||
        
 | 
			
		||||
        print(f"scanner.start_scan returned: {success}")
 | 
			
		||||
        print(f"Scanner status after start attempt: {scanner.status}")
 | 
			
		||||
        
 | 
			
		||||
        if success:
 | 
			
		||||
            scan_session_id = scanner.logger.session_id
 | 
			
		||||
 | 
			
		||||
@ -145,28 +145,29 @@ class Scanner:
 | 
			
		||||
 | 
			
		||||
    def start_scan(self, target_domain: str, max_depth: int = 2, clear_graph: bool = True) -> bool:
 | 
			
		||||
        """
 | 
			
		||||
        Start a new reconnaissance scan with concurrent processing.
 | 
			
		||||
        Enhanced with better debugging and state validation.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            target_domain: Initial domain to investigate
 | 
			
		||||
            max_depth: Maximum recursion depth
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            bool: True if scan started successfully
 | 
			
		||||
        Start a new reconnaissance scan.
 | 
			
		||||
        Forcefully cleans up any previous scan thread before starting.
 | 
			
		||||
        """
 | 
			
		||||
        print(f"=== STARTING SCAN IN SCANNER {id(self)} ===")
 | 
			
		||||
        print(f"Scanner status: {self.status}")
 | 
			
		||||
        print(f"Target domain: '{target_domain}', Max depth: {max_depth}")
 | 
			
		||||
        print(f"Available providers: {len(self.providers) if hasattr(self, 'providers') else 0}")
 | 
			
		||||
        print(f"Initial scanner status: {self.status}")
 | 
			
		||||
 | 
			
		||||
        # If a thread is still alive from a previous scan, we must wait for it to die.
 | 
			
		||||
        if self.scan_thread and self.scan_thread.is_alive():
 | 
			
		||||
            print("A previous scan thread is still alive. Sending termination signal and waiting...")
 | 
			
		||||
            self.stop_scan()
 | 
			
		||||
            self.scan_thread.join(10.0) # Wait up to 10 seconds
 | 
			
		||||
 | 
			
		||||
            if self.scan_thread.is_alive():
 | 
			
		||||
                print("ERROR: The previous scan thread is unresponsive and could not be stopped. Please restart the application.")
 | 
			
		||||
                self.status = ScanStatus.FAILED
 | 
			
		||||
                return False
 | 
			
		||||
            print("Previous scan thread terminated successfully.")
 | 
			
		||||
 | 
			
		||||
        # Reset state for the new scan
 | 
			
		||||
        self.status = ScanStatus.IDLE
 | 
			
		||||
        print(f"Scanner state is now clean for a new scan.")
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            if self.status == ScanStatus.RUNNING:
 | 
			
		||||
                print(f"ERROR: Scan already running in scanner {id(self)}, rejecting new scan")
 | 
			
		||||
                print(f"Current target: {self.current_target}")
 | 
			
		||||
                print(f"Current depth: {self.current_depth}")
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
            # Check if we have any providers
 | 
			
		||||
            if not hasattr(self, 'providers') or not self.providers:
 | 
			
		||||
                print(f"ERROR: No providers available in scanner {id(self)}, cannot start scan")
 | 
			
		||||
@ -174,15 +175,6 @@ class Scanner:
 | 
			
		||||
            
 | 
			
		||||
            print(f"Scanner {id(self)} validation passed, providers available: {[p.get_name() for p in self.providers]}")
 | 
			
		||||
 | 
			
		||||
            # Stop any existing scan thread
 | 
			
		||||
            if self.scan_thread and self.scan_thread.is_alive():
 | 
			
		||||
                print(f"Stopping existing scan thread in scanner {id(self)}...")
 | 
			
		||||
                self.stop_event.set()
 | 
			
		||||
                self.scan_thread.join(timeout=5.0)
 | 
			
		||||
                if self.scan_thread.is_alive():
 | 
			
		||||
                    print(f"WARNING: Could not stop existing thread in scanner {id(self)}")
 | 
			
		||||
                    return False
 | 
			
		||||
 | 
			
		||||
            if clear_graph:
 | 
			
		||||
                self.graph.clear()
 | 
			
		||||
            self.current_target = target_domain.lower().strip()
 | 
			
		||||
@ -212,6 +204,7 @@ class Scanner:
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            print(f"ERROR: Exception in start_scan for scanner {id(self)}: {e}")
 | 
			
		||||
            traceback.print_exc()
 | 
			
		||||
            self.status = ScanStatus.FAILED
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def _execute_scan(self, target_domain: str, max_depth: int) -> None:
 | 
			
		||||
@ -525,7 +518,13 @@ class Scanner:
 | 
			
		||||
        """
 | 
			
		||||
        print(f"Large number of {rel_type.name} relationships for {source}. Creating a large entity node.")
 | 
			
		||||
        entity_name = f"Large collection of {rel_type.name} for {source}"
 | 
			
		||||
        self.graph.add_node(entity_name, NodeType.LARGE_ENTITY, metadata={"count": len(targets)})
 | 
			
		||||
        node_type = 'unknown'
 | 
			
		||||
        if targets:
 | 
			
		||||
            if _is_valid_domain(targets[0]):
 | 
			
		||||
                node_type = 'domain'
 | 
			
		||||
            elif _is_valid_ip(targets[0]):
 | 
			
		||||
                node_type = 'ip'
 | 
			
		||||
        self.graph.add_node(entity_name, NodeType.LARGE_ENTITY, metadata={"count": len(targets), "nodes": targets, "node_type": node_type})
 | 
			
		||||
        self.graph.add_edge(source, entity_name, rel_type, 0.9, provider_name, {"info": "Aggregated node"})
 | 
			
		||||
 | 
			
		||||
    def _safe_provider_query(self, provider, target: str, is_ip: bool) -> List[Tuple[str, str, RelationshipType, float, Dict[str, Any]]]:
 | 
			
		||||
@ -543,32 +542,27 @@ class Scanner:
 | 
			
		||||
 | 
			
		||||
    def stop_scan(self) -> bool:
 | 
			
		||||
        """
 | 
			
		||||
        Request immediate scan termination with aggressive cancellation.
 | 
			
		||||
        Request immediate scan termination.
 | 
			
		||||
        Acts on the thread's liveness, not just the 'RUNNING' status.
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            if self.status == ScanStatus.RUNNING:
 | 
			
		||||
                print("=== INITIATING IMMEDIATE SCAN TERMINATION ===")
 | 
			
		||||
            if not self.scan_thread or not self.scan_thread.is_alive():
 | 
			
		||||
                print("No active scan thread to stop.")
 | 
			
		||||
                # Cleanup state if inconsistent
 | 
			
		||||
                if self.status == ScanStatus.RUNNING:
 | 
			
		||||
                    self.status = ScanStatus.STOPPED
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
                self.stop_event.set()
 | 
			
		||||
            print("=== INITIATING IMMEDIATE SCAN TERMINATION ===")
 | 
			
		||||
            self.status = ScanStatus.STOPPED
 | 
			
		||||
            self.stop_event.set()
 | 
			
		||||
 | 
			
		||||
                for provider in self.providers:
 | 
			
		||||
                    try:
 | 
			
		||||
                        if hasattr(provider, 'session'):
 | 
			
		||||
                            provider.session.close()
 | 
			
		||||
                            print(f"Closed HTTP session for provider: {provider.get_name()}")
 | 
			
		||||
                    except Exception as e:
 | 
			
		||||
                        print(f"Error closing session for {provider.get_name()}: {e}")
 | 
			
		||||
            if self.executor:
 | 
			
		||||
                print("Shutting down executor with immediate cancellation...")
 | 
			
		||||
                self.executor.shutdown(wait=False, cancel_futures=True)
 | 
			
		||||
 | 
			
		||||
                if self.executor:
 | 
			
		||||
                    print("Shutting down executor with immediate cancellation...")
 | 
			
		||||
                    self.executor.shutdown(wait=False, cancel_futures=True)
 | 
			
		||||
                
 | 
			
		||||
                threading.Timer(2.0, self._force_stop_completion).start()
 | 
			
		||||
                
 | 
			
		||||
                print("Immediate termination requested - ongoing requests will be cancelled")
 | 
			
		||||
                return True
 | 
			
		||||
            print("No active scan to stop")
 | 
			
		||||
            return False
 | 
			
		||||
            print("Termination signal sent. The scan thread will stop shortly.")
 | 
			
		||||
            return True
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            print(f"ERROR: Exception in stop_scan: {e}")
 | 
			
		||||
            traceback.print_exc()
 | 
			
		||||
 | 
			
		||||
@ -314,9 +314,39 @@ input[type="text"]:focus, select:focus {
 | 
			
		||||
 | 
			
		||||
.view-controls {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    gap: 1.5rem;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.filter-group {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    gap: 0.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.filter-group label {
 | 
			
		||||
    font-size: 0.9rem;
 | 
			
		||||
    color: #999;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.filter-group select,
 | 
			
		||||
.filter-group input[type="range"] {
 | 
			
		||||
    background-color: #1a1a1a;
 | 
			
		||||
    border: 1px solid #555;
 | 
			
		||||
    color: #c7c7c7;
 | 
			
		||||
    padding: 0.25rem 0.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.filter-group select {
 | 
			
		||||
    max-width: 150px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#confidence-value {
 | 
			
		||||
    min-width: 30px;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    color: #00ff41;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.graph-container {
 | 
			
		||||
    height: 800px;
 | 
			
		||||
    position: relative;
 | 
			
		||||
@ -906,3 +936,38 @@ input[type="text"]:focus, select:focus {
 | 
			
		||||
        opacity: 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* dnsrecon/static/css/main.css */
 | 
			
		||||
 | 
			
		||||
/* ... (at the end of the file) */
 | 
			
		||||
 | 
			
		||||
.large-entity-nodes-list {
 | 
			
		||||
    margin-top: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.large-entity-node-details {
 | 
			
		||||
    margin-bottom: 0.5rem;
 | 
			
		||||
    border: 1px solid #333;
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.large-entity-node-details summary {
 | 
			
		||||
    padding: 0.5rem;
 | 
			
		||||
    background-color: #3a3a3a;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    outline: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.large-entity-node-details summary:hover {
 | 
			
		||||
    background-color: #4a4a4a;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.large-entity-node-details .detail-row {
 | 
			
		||||
    margin-left: 1rem;
 | 
			
		||||
    margin-right: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.large-entity-node-details .detail-section-header {
 | 
			
		||||
    margin-left: 1rem;
 | 
			
		||||
    margin-right: 1rem;
 | 
			
		||||
}
 | 
			
		||||
@ -742,24 +742,19 @@ class DNSReconApp {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show node details modal
 | 
			
		||||
     * @param {string} nodeId - Node identifier
 | 
			
		||||
     * @param {Object} node - Node data
 | 
			
		||||
     * Generates the HTML for the node details view.
 | 
			
		||||
     * @param {Object} node - The node object.
 | 
			
		||||
     * @returns {string} The HTML string for the node details.
 | 
			
		||||
     */
 | 
			
		||||
    showNodeModal(nodeId, node) {
 | 
			
		||||
        if (!this.elements.nodeModal) return;
 | 
			
		||||
 | 
			
		||||
        if (this.elements.modalTitle) {
 | 
			
		||||
            this.elements.modalTitle.textContent = `Node Details`;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    generateNodeDetailsHtml(node) {
 | 
			
		||||
        if(!node) return '<div class="detail-row"><span class="detail-value">Details not available.</span></div>';
 | 
			
		||||
        let detailsHtml = '';
 | 
			
		||||
        const createDetailRow = (label, value, statusIcon = '') => {
 | 
			
		||||
            const baseId = `detail-${label.replace(/[^a-zA-Z0-9]/g, '-')}`;
 | 
			
		||||
            const baseId = `detail-${node.id.replace(/[^a-zA-Z0-9]/g, '-')}-${label.replace(/[^a-zA-Z0-9]/g, '-')}`;
 | 
			
		||||
 | 
			
		||||
            if (value === null || value === undefined ||
 | 
			
		||||
                (Array.isArray(value) && value.length === 0) ||
 | 
			
		||||
                (typeof value === 'object' && Object.keys(value).length === 0)) {
 | 
			
		||||
                (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0)) {
 | 
			
		||||
                return `
 | 
			
		||||
                    <div class="detail-row">
 | 
			
		||||
                        <span class="detail-label">${label} <span class="status-icon text-warning">✗</span></span>
 | 
			
		||||
@ -795,10 +790,8 @@ class DNSReconApp {
 | 
			
		||||
 | 
			
		||||
        const metadata = node.metadata || {};
 | 
			
		||||
 | 
			
		||||
        // General Node Info
 | 
			
		||||
        detailsHtml += createDetailRow('Node Descriptor', node.nodeId);
 | 
			
		||||
        detailsHtml += createDetailRow('Node Descriptor', node.id);
 | 
			
		||||
 | 
			
		||||
        // Display data based on node type
 | 
			
		||||
        switch (node.type) {
 | 
			
		||||
            case 'domain':
 | 
			
		||||
                detailsHtml += createDetailRow('DNS Records', metadata.dns_records);
 | 
			
		||||
@ -815,7 +808,6 @@ class DNSReconApp {
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Special handling for certificate data
 | 
			
		||||
        if (metadata.certificate_data && Object.keys(metadata.certificate_data).length > 0) {
 | 
			
		||||
            const cert = metadata.certificate_data;
 | 
			
		||||
            detailsHtml += `<div class="detail-section-header">Certificate Summary</div>`;
 | 
			
		||||
@ -833,7 +825,6 @@ class DNSReconApp {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Special handling for ASN data
 | 
			
		||||
        if (metadata.asn_data && Object.keys(metadata.asn_data).length > 0) {
 | 
			
		||||
            detailsHtml += `<div class="detail-section-header">ASN Information</div>`;
 | 
			
		||||
            detailsHtml += createDetailRow('ASN', metadata.asn_data.asn);
 | 
			
		||||
@ -842,6 +833,44 @@ class DNSReconApp {
 | 
			
		||||
            detailsHtml += createDetailRow('Country', metadata.asn_data.country);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return detailsHtml;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Show node details modal
 | 
			
		||||
     * @param {string} nodeId - Node identifier
 | 
			
		||||
     * @param {Object} node - Node data
 | 
			
		||||
     */
 | 
			
		||||
    showNodeModal(nodeId, node) {
 | 
			
		||||
        if (!this.elements.nodeModal) return;
 | 
			
		||||
 | 
			
		||||
        if (this.elements.modalTitle) {
 | 
			
		||||
            this.elements.modalTitle.textContent = `Node Details`;
 | 
			
		||||
        }
 | 
			
		||||
        let detailsHtml = '';
 | 
			
		||||
 | 
			
		||||
        if (node.type === 'large_entity') {
 | 
			
		||||
            const metadata = node.metadata || {};
 | 
			
		||||
            const nodes = metadata.nodes || [];
 | 
			
		||||
            const node_type = metadata.node_type || 'nodes';
 | 
			
		||||
            detailsHtml += `<div class="detail-section-header">Contains ${metadata.count} ${node_type}s</div>`;
 | 
			
		||||
            detailsHtml += '<div class="large-entity-nodes-list">';
 | 
			
		||||
 | 
			
		||||
            for(const innerNodeId of nodes) {
 | 
			
		||||
                const innerNode = this.graphManager.nodes.get(innerNodeId);
 | 
			
		||||
                detailsHtml += `<details class="large-entity-node-details">`;
 | 
			
		||||
                detailsHtml += `<summary>${innerNodeId}</summary>`;
 | 
			
		||||
                detailsHtml += this.generateNodeDetailsHtml(innerNode);
 | 
			
		||||
                detailsHtml += `</details>`;
 | 
			
		||||
            }
 | 
			
		||||
            detailsHtml += '</div>';
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
             detailsHtml = this.generateNodeDetailsHtml(node);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if (this.elements.modalDetails) {
 | 
			
		||||
            this.elements.modalDetails.innerHTML = detailsHtml;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user