attempt to fix some logic
This commit is contained in:
@@ -382,7 +382,6 @@ class GraphManager {
|
||||
|
||||
graphData.nodes.forEach(node => {
|
||||
if (node.type === 'large_entity' && node.attributes) {
|
||||
// UPDATED: Handle unified data model - look for 'nodes' attribute in the attributes list
|
||||
const nodesAttribute = this.findAttributeByName(node.attributes, 'nodes');
|
||||
if (nodesAttribute && Array.isArray(nodesAttribute.value)) {
|
||||
nodesAttribute.value.forEach(nodeId => {
|
||||
@@ -394,15 +393,30 @@ class GraphManager {
|
||||
});
|
||||
|
||||
const filteredNodes = graphData.nodes.filter(node => {
|
||||
// Only include nodes that are NOT members of large entities, but always include the container itself
|
||||
return !this.largeEntityMembers.has(node.id) || node.type === 'large_entity';
|
||||
});
|
||||
|
||||
console.log(`Filtered ${graphData.nodes.length - filteredNodes.length} large entity member nodes from visualization`);
|
||||
|
||||
// Process only the filtered nodes
|
||||
// FIXED: Process nodes with proper certificate coloring
|
||||
const processedNodes = filteredNodes.map(node => {
|
||||
return this.processNode(node);
|
||||
const processed = this.processNode(node);
|
||||
|
||||
// FIXED: Apply certificate-based coloring here in frontend
|
||||
if (node.type === 'domain' && Array.isArray(node.attributes)) {
|
||||
const certInfo = this.analyzeCertificateInfo(node.attributes);
|
||||
|
||||
if (certInfo.hasExpiredOnly) {
|
||||
// Red for domains with only expired/invalid certificates
|
||||
processed.color = { background: '#ff6b6b', border: '#cc5555' };
|
||||
} else if (!certInfo.hasCertificates) {
|
||||
// Grey for domains with no certificates
|
||||
processed.color = { background: '#c7c7c7', border: '#999999' };
|
||||
}
|
||||
// Valid certificates use default green (handled by processNode)
|
||||
}
|
||||
|
||||
return processed;
|
||||
});
|
||||
|
||||
const mergedEdges = {};
|
||||
@@ -439,24 +453,19 @@ class GraphManager {
|
||||
const existingNodeIds = this.nodes.getIds();
|
||||
const existingEdgeIds = this.edges.getIds();
|
||||
|
||||
// Add new nodes with fade-in animation
|
||||
const newNodes = processedNodes.filter(node => !existingNodeIds.includes(node.id));
|
||||
const newEdges = processedEdges.filter(edge => !existingEdgeIds.includes(edge.id));
|
||||
|
||||
// Update existing data
|
||||
this.nodes.update(processedNodes);
|
||||
this.edges.update(processedEdges);
|
||||
|
||||
// After data is loaded, apply filters
|
||||
this.updateFilterControls();
|
||||
this.applyAllFilters();
|
||||
|
||||
// Highlight new additions briefly
|
||||
if (newNodes.length > 0 || newEdges.length > 0) {
|
||||
setTimeout(() => this.highlightNewElements(newNodes, newEdges), 100);
|
||||
}
|
||||
|
||||
// Auto-fit view for small graphs or first update
|
||||
if (processedNodes.length <= 10 || existingNodeIds.length === 0) {
|
||||
setTimeout(() => this.fitView(), 800);
|
||||
}
|
||||
@@ -470,6 +479,46 @@ class GraphManager {
|
||||
}
|
||||
}
|
||||
|
||||
analyzeCertificateInfo(attributes) {
|
||||
let hasCertificates = false;
|
||||
let hasValidCertificates = false;
|
||||
let hasExpiredCertificates = false;
|
||||
|
||||
for (const attr of attributes) {
|
||||
const attrName = (attr.name || '').toLowerCase();
|
||||
const attrProvider = (attr.provider || '').toLowerCase();
|
||||
const attrValue = attr.value;
|
||||
|
||||
// Look for certificate attributes from crtsh provider
|
||||
if (attrProvider === 'crtsh' || attrName.startsWith('cert_')) {
|
||||
hasCertificates = true;
|
||||
|
||||
// Check certificate validity using raw attribute names
|
||||
if (attrName === 'cert_is_currently_valid') {
|
||||
if (attrValue === true) {
|
||||
hasValidCertificates = true;
|
||||
} else if (attrValue === false) {
|
||||
hasExpiredCertificates = true;
|
||||
}
|
||||
}
|
||||
// Check for expiry indicators
|
||||
else if (attrName === 'cert_expires_soon' && attrValue === true) {
|
||||
hasExpiredCertificates = true;
|
||||
}
|
||||
else if (attrName.includes('expired') && attrValue === true) {
|
||||
hasExpiredCertificates = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hasCertificates,
|
||||
hasValidCertificates,
|
||||
hasExpiredCertificates,
|
||||
hasExpiredOnly: hasExpiredCertificates && !hasValidCertificates
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* UPDATED: Helper method to find an attribute by name in the standardized attributes list
|
||||
* @param {Array} attributes - List of StandardAttribute objects
|
||||
@@ -496,7 +545,7 @@ class GraphManager {
|
||||
size: this.getNodeSize(node.type),
|
||||
borderColor: this.getNodeBorderColor(node.type),
|
||||
shape: this.getNodeShape(node.type),
|
||||
attributes: node.attributes || [], // Keep as standardized attributes list
|
||||
attributes: node.attributes || [],
|
||||
description: node.description || '',
|
||||
metadata: node.metadata || {},
|
||||
type: node.type,
|
||||
@@ -509,19 +558,33 @@ class GraphManager {
|
||||
processedNode.borderWidth = Math.max(2, Math.floor(node.confidence * 5));
|
||||
}
|
||||
|
||||
// Handle merged correlation objects (similar to large entities)
|
||||
// FIXED: Certificate-based domain coloring
|
||||
if (node.type === 'domain' && Array.isArray(node.attributes)) {
|
||||
const certInfo = this.analyzeCertificateInfo(node.attributes);
|
||||
|
||||
if (certInfo.hasExpiredOnly) {
|
||||
// Red for domains with only expired/invalid certificates
|
||||
processedNode.color = '#ff6b6b';
|
||||
processedNode.borderColor = '#cc5555';
|
||||
} else if (!certInfo.hasCertificates) {
|
||||
// Grey for domains with no certificates
|
||||
processedNode.color = '#c7c7c7';
|
||||
processedNode.borderColor = '#999999';
|
||||
}
|
||||
// Green for valid certificates (default color)
|
||||
}
|
||||
|
||||
// Handle merged correlation objects
|
||||
if (node.type === 'correlation_object') {
|
||||
const metadata = node.metadata || {};
|
||||
const values = metadata.values || [];
|
||||
const mergeCount = metadata.merge_count || 1;
|
||||
|
||||
if (mergeCount > 1) {
|
||||
// Display as merged correlation container
|
||||
processedNode.label = `Correlations (${mergeCount})`;
|
||||
processedNode.title = `Merged correlation container with ${mergeCount} values: ${values.slice(0, 3).join(', ')}${values.length > 3 ? '...' : ''}`;
|
||||
processedNode.borderWidth = 3; // Thicker border for merged nodes
|
||||
processedNode.borderWidth = 3;
|
||||
} else {
|
||||
// Single correlation value
|
||||
const value = Array.isArray(values) && values.length > 0 ? values[0] : (metadata.value || 'Unknown');
|
||||
const displayValue = typeof value === 'string' && value.length > 20 ? value.substring(0, 17) + '...' : value;
|
||||
processedNode.label = `${displayValue}`;
|
||||
@@ -532,6 +595,7 @@ class GraphManager {
|
||||
return processedNode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process edge data with styling and metadata
|
||||
* @param {Object} edge - Raw edge data
|
||||
|
||||
@@ -868,20 +868,14 @@ class DNSReconApp {
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* NEW: Organized attributes section with provider/semantic grouping (no formatting)
|
||||
*/
|
||||
generateOrganizedAttributesSection(attributes, nodeType) {
|
||||
if (!Array.isArray(attributes) || attributes.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Group attributes intelligently
|
||||
const groups = this.groupAttributesByProviderAndType(attributes, nodeType);
|
||||
|
||||
let html = '';
|
||||
|
||||
// Sort groups by priority
|
||||
const sortedGroups = Object.entries(groups).sort((a, b) => {
|
||||
const priorityOrder = { 'high': 0, 'medium': 1, 'low': 2 };
|
||||
return priorityOrder[a[1].priority] - priorityOrder[b[1].priority];
|
||||
@@ -904,22 +898,10 @@ class DNSReconApp {
|
||||
`;
|
||||
|
||||
groupData.attributes.forEach(attr => {
|
||||
// Format the value appropriately
|
||||
let displayValue = '';
|
||||
if (attr.value === null || attr.value === undefined) {
|
||||
displayValue = 'N/A';
|
||||
} else if (Array.isArray(attr.value)) {
|
||||
displayValue = attr.value.length > 0 ? `Array (${attr.value.length} items)` : 'Empty Array';
|
||||
} else if (typeof attr.value === 'object') {
|
||||
displayValue = 'Object';
|
||||
} else {
|
||||
displayValue = String(attr.value);
|
||||
}
|
||||
|
||||
html += `
|
||||
<div class="attribute-item-compact">
|
||||
<span class="attribute-key-compact">${this.escapeHtml(attr.name || 'Unknown')}</span>
|
||||
<span class="attribute-value-compact">${this.escapeHtml(displayValue)}</span>
|
||||
<span class="attribute-value-compact">${this.formatAttributeValue(attr)}</span>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
@@ -930,12 +912,49 @@ class DNSReconApp {
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* NEW: Group attributes by provider and semantic meaning (no formatting)
|
||||
*/
|
||||
formatAttributeValue(attr) {
|
||||
const value = attr.value;
|
||||
const name = attr.name || '';
|
||||
|
||||
if (value === null || value === undefined) {
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
if (value.length === 0) {
|
||||
return 'Empty Array';
|
||||
}
|
||||
|
||||
// Special handling for DNS records and similar arrays
|
||||
if (name === 'dns_records' || name.includes('record') || name.includes('hostname')) {
|
||||
// Show DNS records as a readable list
|
||||
if (value.length <= 5) {
|
||||
return this.escapeHtml(value.join(', '));
|
||||
} else {
|
||||
const preview = value.slice(0, 3).join(', ');
|
||||
return this.escapeHtml(`${preview} ... (+${value.length - 3} more)`);
|
||||
}
|
||||
}
|
||||
|
||||
// For other arrays
|
||||
if (value.length <= 3) {
|
||||
return this.escapeHtml(value.join(', '));
|
||||
} else {
|
||||
const preview = value.slice(0, 2).join(', ');
|
||||
return this.escapeHtml(`${preview} ... (${value.length} total)`);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
return 'Object';
|
||||
}
|
||||
|
||||
return this.escapeHtml(String(value));
|
||||
}
|
||||
|
||||
groupAttributesByProviderAndType(attributes, nodeType) {
|
||||
const groups = {
|
||||
'DNS Records': { icon: '🔍', priority: 'high', attributes: [] },
|
||||
'DNS Records': { icon: '📋', priority: 'high', attributes: [] },
|
||||
'Certificate Information': { icon: '🔒', priority: 'high', attributes: [] },
|
||||
'Network Information': { icon: '🌐', priority: 'high', attributes: [] },
|
||||
'Provider Data': { icon: '📊', priority: 'medium', attributes: [] },
|
||||
@@ -943,23 +962,29 @@ class DNSReconApp {
|
||||
};
|
||||
|
||||
for (const attr of attributes) {
|
||||
const provider = attr.provider?.toLowerCase() || '';
|
||||
const name = attr.name?.toLowerCase() || '';
|
||||
const provider = (attr.provider || '').toLowerCase();
|
||||
const name = (attr.name || '').toLowerCase();
|
||||
const type = (attr.type || '').toLowerCase();
|
||||
|
||||
let assigned = false;
|
||||
|
||||
// DNS-related attributes
|
||||
if (provider === 'dns' || ['dns', 'record', 'ptr', 'mx', 'cname', 'ns', 'txt', 'soa'].some(keyword => name.includes(keyword))) {
|
||||
// DNS-related attributes (better detection)
|
||||
if (provider === 'dns' ||
|
||||
name === 'dns_records' ||
|
||||
name.includes('record') ||
|
||||
['ptr', 'mx', 'cname', 'ns', 'txt', 'soa'].some(keyword => name.includes(keyword))) {
|
||||
groups['DNS Records'].attributes.push(attr);
|
||||
assigned = true;
|
||||
}
|
||||
// Certificate-related attributes
|
||||
else if (provider === 'crtsh' || ['cert', 'certificate', 'ssl', 'tls', 'issuer', 'validity', 'san'].some(keyword => name.includes(keyword))) {
|
||||
else if (provider === 'crtsh' || name.startsWith('cert_') ||
|
||||
['certificate', 'ssl', 'tls', 'issuer', 'validity', 'san'].some(keyword => name.includes(keyword))) {
|
||||
groups['Certificate Information'].attributes.push(attr);
|
||||
assigned = true;
|
||||
}
|
||||
// Network/Shodan attributes
|
||||
else if (provider === 'shodan' || ['port', 'service', 'banner', 'asn', 'organization', 'country', 'city', 'network'].some(keyword => name.includes(keyword))) {
|
||||
else if (provider === 'shodan' ||
|
||||
['port', 'service', 'banner', 'asn', 'organization', 'country', 'city', 'network'].some(keyword => name.includes(keyword))) {
|
||||
groups['Network Information'].attributes.push(attr);
|
||||
assigned = true;
|
||||
}
|
||||
@@ -985,6 +1010,33 @@ class DNSReconApp {
|
||||
return groups;
|
||||
}
|
||||
|
||||
formatEdgeLabel(relationshipType, confidence) {
|
||||
if (!relationshipType) return '';
|
||||
|
||||
// UPDATED: No formatting of relationship type - use raw values
|
||||
const confidenceText = confidence >= 0.8 ? '●' : confidence >= 0.6 ? '◐' : '○';
|
||||
return `${relationshipType} ${confidenceText}`;
|
||||
}
|
||||
|
||||
createEdgeTooltip(edge) {
|
||||
let tooltip = `<div style="font-family: 'Roboto Mono', monospace; font-size: 11px;">`;
|
||||
tooltip += `<div style="color: #00ff41; font-weight: bold; margin-bottom: 4px;">${edge.label || 'Relationship'}</div>`;
|
||||
tooltip += `<div style="color: #999; margin-bottom: 2px;">Confidence: ${(edge.confidence_score * 100).toFixed(1)}%</div>`;
|
||||
|
||||
// UPDATED: Use raw provider name (no formatting)
|
||||
if (edge.source_provider) {
|
||||
tooltip += `<div style="color: #999; margin-bottom: 2px;">Provider: ${edge.source_provider}</div>`;
|
||||
}
|
||||
|
||||
if (edge.discovery_timestamp) {
|
||||
const date = new Date(edge.discovery_timestamp);
|
||||
tooltip += `<div style="color: #666; font-size: 10px;">Discovered: ${date.toLocaleString()}</div>`;
|
||||
}
|
||||
|
||||
tooltip += `</div>`;
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* UPDATED: Enhanced correlation details showing the correlated attribute clearly (no formatting)
|
||||
*/
|
||||
@@ -996,7 +1048,7 @@ class DNSReconApp {
|
||||
|
||||
let html = '';
|
||||
|
||||
// Show what attribute is being correlated
|
||||
// Show what attribute is being correlated (raw names)
|
||||
const primarySource = metadata.primary_source || 'unknown';
|
||||
|
||||
html += `
|
||||
@@ -1013,7 +1065,7 @@ class DNSReconApp {
|
||||
<span class="attribute-value-compact"><code>${this.escapeHtml(String(value))}</code></span>
|
||||
</div>
|
||||
<div class="attribute-item-compact">
|
||||
<span class="attribute-key-compact">Attribute Type</span>
|
||||
<span class="attribute-key-compact">Attribute Source</span>
|
||||
<span class="attribute-value-compact">${primarySource}</span>
|
||||
</div>
|
||||
<div class="attribute-item-compact">
|
||||
@@ -1050,10 +1102,6 @@ class DNSReconApp {
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* UPDATED: Generate large entity details using unified data model
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user