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
|
||||
|
102
core/scanner.py
102
core/scanner.py
@ -145,44 +145,36 @@ 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}")
|
||||
|
||||
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
|
||||
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:
|
||||
# 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")
|
||||
return False
|
||||
|
||||
|
||||
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 ===")
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
print("=== INITIATING IMMEDIATE SCAN TERMINATION ===")
|
||||
self.status = ScanStatus.STOPPED
|
||||
self.stop_event.set()
|
||||
|
||||
if self.executor:
|
||||
print("Shutting down executor with immediate cancellation...")
|
||||
self.executor.shutdown(wait=False, cancel_futures=True)
|
||||
|
||||
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;
|
||||
@ -905,4 +935,39 @@ input[type="text"]:focus, select:focus {
|
||||
transform: translateX(100%);
|
||||
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;
|
||||
}
|
@ -740,26 +740,21 @@ class DNSReconApp {
|
||||
this.elements.providerList.appendChild(providerItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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>
|
||||
@ -794,11 +789,9 @@ class DNSReconApp {
|
||||
};
|
||||
|
||||
const metadata = node.metadata || {};
|
||||
|
||||
// General Node Info
|
||||
detailsHtml += createDetailRow('Node Descriptor', node.nodeId);
|
||||
|
||||
// Display data based on node type
|
||||
detailsHtml += createDetailRow('Node Descriptor', node.id);
|
||||
|
||||
switch (node.type) {
|
||||
case 'domain':
|
||||
detailsHtml += createDetailRow('DNS Records', metadata.dns_records);
|
||||
@ -814,8 +807,7 @@ class DNSReconApp {
|
||||
detailsHtml += createDetailRow('VirusTotal Data', metadata.virustotal);
|
||||
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>`;
|
||||
@ -832,8 +824,7 @@ class DNSReconApp {
|
||||
detailsHtml += createDetailRow('Valid Until', new Date(cert.latest_certificate.not_after).toLocaleString());
|
||||
}
|
||||
}
|
||||
|
||||
// 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