This commit is contained in:
overcuriousity
2025-09-11 14:01:15 +02:00
parent 2d485c5703
commit d3e1fcf35f
18 changed files with 1806 additions and 843 deletions

View File

@@ -318,17 +318,13 @@ input[type="text"]:focus, select:focus {
}
.graph-container {
height: 500px;
height: 800px;
position: relative;
background-color: #1a1a1a;
border-top: 1px solid #444;
transition: height 0.3s ease;
}
.graph-container.expanded {
height: 700px;
}
.graph-controls {
position: absolute;
top: 10px;
@@ -535,29 +531,6 @@ input[type="text"]:focus, select:focus {
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
}
.node-info-title {
color: #00ff41;
font-weight: bold;
margin-bottom: 0.5rem;
border-bottom: 1px solid #444;
padding-bottom: 0.25rem;
}
.node-info-detail {
margin-bottom: 0.25rem;
display: flex;
justify-content: space-between;
}
.node-info-label {
color: #999;
}
.node-info-value {
color: #c7c7c7;
font-weight: 500;
}
/* Footer */
.footer {
background-color: #0a0a0a;

View File

@@ -233,7 +233,6 @@ class GraphManager {
const nodeId = params.node;
const node = this.nodes.get(nodeId);
if (node) {
this.showNodeInfoPopup(params.pointer.DOM, node);
this.highlightConnectedNodes(nodeId, true);
}
});
@@ -243,19 +242,6 @@ class GraphManager {
this.clearHoverHighlights();
});
// Edge hover events
this.network.on('hoverEdge', (params) => {
const edgeId = params.edge;
const edge = this.edges.get(edgeId);
if (edge) {
this.showEdgeInfo(params.pointer.DOM, edge);
}
});
this.network.on('blurEdge', () => {
this.hideNodeInfoPopup();
});
// Double-click to focus on node
this.network.on('doubleClick', (params) => {
if (params.nodes.length > 0) {
@@ -347,7 +333,6 @@ class GraphManager {
const processedNode = {
id: node.id,
label: this.formatNodeLabel(node.id, node.type),
title: this.createNodeTooltip(node),
color: this.getNodeColor(node.type),
size: this.getNodeSize(node.type),
borderColor: this.getNodeBorderColor(node.type),
@@ -373,11 +358,14 @@ class GraphManager {
}
// Style based on certificate validity
if (node.has_valid_cert === true) {
processedNode.borderColor = '#00ff41'; // Green for valid cert
} else if (node.has_valid_cert === false) {
processedNode.borderColor = '#ff9900'; // Amber for expired/no cert
processedNode.borderDashes = [5, 5];
if (node.type === 'domain') {
if (node.metadata && node.metadata.has_valid_cert === true) {
processedNode.color = '#00ff41'; // Bright green for valid cert
processedNode.borderColor = '#00aa2e';
} else if (node.metadata && node.metadata.has_valid_cert === false) {
processedNode.color = '#888888'; // Muted grey color
processedNode.borderColor = '#666666'; // Darker grey border
}
}
return processedNode;
@@ -457,9 +445,9 @@ class GraphManager {
const colors = {
'domain': '#00ff41', // Green
'ip': '#ff9900', // Amber
'certificate': '#c7c7c7', // Gray
'asn': '#00aaff', // Blue
'large_entity': '#ff6b6b' // Red for large entities
'large_entity': '#ff6b6b', // Red for large entities
'dns_record': '#999999'
};
return colors[nodeType] || '#ffffff';
}
@@ -474,8 +462,8 @@ class GraphManager {
const borderColors = {
'domain': '#00aa2e',
'ip': '#cc7700',
'certificate': '#999999',
'asn': '#0088cc'
'asn': '#0088cc',
'dns_record': '#999999'
};
return borderColors[nodeType] || '#666666';
}
@@ -489,8 +477,8 @@ class GraphManager {
const sizes = {
'domain': 12,
'ip': 14,
'certificate': 10,
'asn': 16
'asn': 16,
'dns_record': 8
};
return sizes[nodeType] || 12;
}
@@ -504,8 +492,8 @@ class GraphManager {
const shapes = {
'domain': 'dot',
'ip': 'square',
'certificate': 'diamond',
'asn': 'triangle'
'asn': 'triangle',
'dns_record': 'hexagon'
};
return shapes[nodeType] || 'dot';
}
@@ -541,26 +529,7 @@ class GraphManager {
}
/**
* Create node tooltip
* @param {Object} node - Node data
* @returns {string} HTML tooltip content
*/
createNodeTooltip(node) {
let tooltip = `<div style="font-family: 'Roboto Mono', monospace; font-size: 11px;">`;
tooltip += `<div style="color: #00ff41; font-weight: bold; margin-bottom: 4px;">${node.id}</div>`;
tooltip += `<div style="color: #999; margin-bottom: 2px;">Type: ${node.type}</div>`;
if (node.metadata && Object.keys(node.metadata).length > 0) {
tooltip += `<div style="color: #999; margin-top: 4px; border-top: 1px solid #444; padding-top: 4px;">`;
tooltip += `Click for details</div>`;
}
tooltip += `</div>`;
return tooltip;
}
/**
* Create edge tooltip
* Create edge tooltip with correct provider information
* @param {Object} edge - Edge data
* @returns {string} HTML tooltip content
*/
@@ -570,7 +539,7 @@ class GraphManager {
tooltip += `<div style="color: #999; margin-bottom: 2px;">Confidence: ${(edge.confidence_score * 100).toFixed(1)}%</div>`;
if (edge.source_provider) {
tooltip += `<div style="color: #999; margin-bottom: 2px;">Source: ${edge.source_provider}</div>`;
tooltip += `<div style="color: #999; margin-bottom: 2px;">Provider: ${edge.source_provider}</div>`;
}
if (edge.discovery_timestamp) {
@@ -610,69 +579,6 @@ class GraphManager {
document.dispatchEvent(event);
}
/**
* Show enhanced node info popup
* @param {Object} position - Mouse position
* @param {Object} node - Node data
*/
showNodeInfoPopup(position, node) {
if (!this.nodeInfoPopup) return;
const html = `
<div class="node-info-title">${node.id}</div>
<div class="node-info-detail">
<span class="node-info-label">Type:</span>
<span class="node-info-value">${node.type || 'Unknown'}</span>
</div>
${node.metadata && Object.keys(node.metadata).length > 0 ?
'<div class="node-info-detail"><span class="node-info-label">Details:</span><span class="node-info-value">Click for more</span></div>' :
''}
`;
this.nodeInfoPopup.innerHTML = html;
this.nodeInfoPopup.style.display = 'block';
this.nodeInfoPopup.style.left = position.x + 15 + 'px';
this.nodeInfoPopup.style.top = position.y - 10 + 'px';
// Ensure popup stays in viewport
const rect = this.nodeInfoPopup.getBoundingClientRect();
if (rect.right > window.innerWidth) {
this.nodeInfoPopup.style.left = position.x - rect.width - 15 + 'px';
}
if (rect.bottom > window.innerHeight) {
this.nodeInfoPopup.style.top = position.y - rect.height + 10 + 'px';
}
}
/**
* Show edge information tooltip
* @param {Object} position - Mouse position
* @param {Object} edge - Edge data
*/
showEdgeInfo(position, edge) {
if (!this.nodeInfoPopup) return;
const confidence = edge.metadata ? edge.metadata.confidence_score : 0;
const provider = edge.metadata ? edge.metadata.source_provider : 'Unknown';
const html = `
<div class="node-info-title">${edge.metadata ? edge.metadata.relationship_type : 'Relationship'}</div>
<div class="node-info-detail">
<span class="node-info-label">Confidence:</span>
<span class="node-info-value">${(confidence * 100).toFixed(1)}%</span>
</div>
<div class="node-info-detail">
<span class="node-info-label">Provider:</span>
<span class="node-info-value">${provider}</span>
</div>
`;
this.nodeInfoPopup.innerHTML = html;
this.nodeInfoPopup.style.display = 'block';
this.nodeInfoPopup.style.left = position.x + 15 + 'px';
this.nodeInfoPopup.style.top = position.y - 10 + 'px';
}
/**
* Hide node info popup
*/

View File

@@ -511,9 +511,18 @@ class DNSReconApp {
}
}
// Update session ID
if (this.currentSessionId && this.elements.sessionId) {
this.elements.sessionId.textContent = `Session: ${this.currentSessionId}`;
// Update session ID display with user session info
if (this.elements.sessionId) {
const scanSessionId = this.currentSessionId;
const userSessionId = status.user_session_id;
if (scanSessionId && userSessionId) {
this.elements.sessionId.textContent = `Session: ${userSessionId.substring(0, 8)}... | Scan: ${scanSessionId}`;
} else if (userSessionId) {
this.elements.sessionId.textContent = `User Session: ${userSessionId.substring(0, 8)}...`;
} else {
this.elements.sessionId.textContent = 'Session: Loading...';
}
}
console.log('Status display updated successfully');
@@ -730,11 +739,13 @@ class DNSReconApp {
}
let detailsHtml = '';
const createDetailRow = (label, value) => {
const createDetailRow = (label, value, statusIcon = '') => {
const baseId = `detail-${label.replace(/[^a-zA-Z0-9]/g, '-')}`;
// Handle empty or undefined values by showing N/A
if (value === null || value === undefined || (Array.isArray(value) && value.length === 0)) {
// Handle empty or undefined values
if (value === null || value === undefined ||
(Array.isArray(value) && value.length === 0) ||
(typeof value === 'object' && Object.keys(value).length === 0)) {
return `
<div class="detail-row">
<span class="detail-label">${label} <span class="status-icon text-warning">✗</span></span>
@@ -743,12 +754,11 @@ class DNSReconApp {
`;
}
// If value is an array, create a row for each item
// Handle arrays
if (Array.isArray(value)) {
return value.map((item, index) => {
const itemId = `${baseId}-${index}`;
// Only show the label for the first item in the list
const itemLabel = index === 0 ? label : '';
const itemLabel = index === 0 ? `${label} <span class="status-icon text-success">✓</span>` : '';
return `
<div class="detail-row">
<span class="detail-label">${itemLabel}</span>
@@ -758,12 +768,13 @@ class DNSReconApp {
`;
}).join('');
}
// Handle objects and other primitive values in a single row
// Handle objects and primitives
else {
const valueId = `${baseId}-0`;
const icon = statusIcon || '<span class="status-icon text-success">✓</span>';
return `
<div class="detail-row">
<span class="detail-label">${label} <span class="status-icon text-success">✓</span></span>
<span class="detail-label">${label} ${icon}</span>
<span class="detail-value" id="${valueId}">${this.formatValue(value)}</span>
<button class="copy-btn" onclick="copyToClipboard('${valueId}')" title="Copy">📋</button>
</div>
@@ -773,33 +784,44 @@ class DNSReconApp {
const metadata = node.metadata || {};
// Display data based on node type
switch (node.type) {
case 'domain':
detailsHtml += createDetailRow('DNS Records', metadata.dns_records);
detailsHtml += createDetailRow('Related Domains (SAN)', metadata.related_domains_san);
detailsHtml += createDetailRow('Certificate Data', metadata.certificate_data);
detailsHtml += createDetailRow('Passive DNS', metadata.passive_dns);
detailsHtml += createDetailRow('Shodan Data', metadata.shodan);
detailsHtml += createDetailRow('VirusTotal Data', metadata.virustotal);
detailsHtml += createDetailRow('ASN Information', metadata.asn_data);
break;
case 'ip':
detailsHtml += createDetailRow('DNS Records', metadata.dns_records);
detailsHtml += createDetailRow('Passive DNS', metadata.passive_dns);
detailsHtml += createDetailRow('Shodan Data', metadata.shodan);
detailsHtml += createDetailRow('VirusTotal Data', metadata.virustotal);
break;
case 'certificate':
detailsHtml += createDetailRow('Certificate Hash', metadata.hash);
detailsHtml += createDetailRow('SANs', metadata.sans);
detailsHtml += createDetailRow('Issuer', metadata.issuer);
detailsHtml += createDetailRow('Validity', `From: ${metadata.not_before || 'N/A'} To: ${metadata.not_after || 'N/A'}`);
detailsHtml += createDetailRow('ASN Information', metadata.asn_data);
break;
case 'asn':
detailsHtml += createDetailRow('ASN', metadata.asn);
detailsHtml += createDetailRow('Description', metadata.description);
detailsHtml += createDetailRow('ASN Information', metadata.asn_data);
detailsHtml += createDetailRow('Related IPs', metadata.passive_dns);
break;
case 'large_entity':
detailsHtml += createDetailRow('Entity Type', metadata.entity_type || 'Large Collection');
detailsHtml += createDetailRow('Item Count', metadata.count);
detailsHtml += createDetailRow('Discovered Domains', metadata.domains);
break;
}
// Add any additional metadata not covered above
for (const [key, value] of Object.entries(metadata)) {
if (!['dns_records', 'related_domains_san', 'certificate_data', 'passive_dns',
'shodan', 'virustotal', 'asn_data', 'hash', 'sans', 'issuer',
'not_before', 'not_after', 'entity_type', 'count', 'domains'].includes(key)) {
detailsHtml += createDetailRow(this.formatLabel(key), value);
}
}
if (this.elements.modalDetails) {
this.elements.modalDetails.innerHTML = detailsHtml;
}