diff --git a/core/graph_manager.py b/core/graph_manager.py index fe6028a..954f744 100644 --- a/core/graph_manager.py +++ b/core/graph_manager.py @@ -18,7 +18,8 @@ class NodeType(Enum): """Enumeration of supported node types.""" DOMAIN = "domain" IP = "ip" - ASN = "asn" + ISP = "isp" + CA = "ca" LARGE_ENTITY = "large_entity" CORRELATION_OBJECT = "correlation_object" diff --git a/core/scanner.py b/core/scanner.py index 57dc0e5..794f98b 100644 --- a/core/scanner.py +++ b/core/scanner.py @@ -546,7 +546,7 @@ class Scanner: self._update_provider_state(target, provider_name, 'failed', 0, str(e), start_time) return None - def _process_provider_result_unified(self, target: str, provider: BaseProvider, + def _process_provider_result_unified(self, target: str, provider: BaseProvider, provider_result: ProviderResult, current_depth: int) -> Tuple[Set[str], bool]: """ Process a unified ProviderResult object to update the graph. @@ -573,11 +573,9 @@ class Scanner: if self.graph.graph.has_node(node_id): if _is_valid_ip(node_id): node_type = NodeType.IP - elif node_id.startswith('AS') and node_id[2:].isdigit(): - node_type = NodeType.ASN else: node_type = NodeType.DOMAIN - + self.graph.add_node(node_id, node_type, attributes=node_attributes_list) if provider_result.get_relationship_count() > self.config.large_entity_threshold: @@ -590,27 +588,30 @@ class Scanner: source_node = relationship.source_node target_node = relationship.target_node - + source_type = NodeType.IP if _is_valid_ip(source_node) else NodeType.DOMAIN - if target_node.startswith('AS') and target_node[2:].isdigit(): - target_type = NodeType.ASN + + if provider_name == 'shodan' and relationship.relationship_type == 'ip_to_isp': + target_type = NodeType.ISP + elif provider_name == 'crtsh' and relationship.relationship_type == 'issued_by': + target_type = NodeType.CA elif _is_valid_ip(target_node): target_type = NodeType.IP else: target_type = NodeType.DOMAIN - + self.graph.add_node(source_node, source_type) self.graph.add_node(target_node, target_type) - + if self.graph.add_edge( - source_node, target_node, - relationship.relationship_type, - relationship.confidence, - provider_name, + source_node, target_node, + relationship.relationship_type, + relationship.confidence, + provider_name, relationship.raw_data ): pass - + if _is_valid_domain(target_node) or _is_valid_ip(target_node): discovered_targets.add(target_node) diff --git a/providers/crtsh_provider.py b/providers/crtsh_provider.py index 26e2590..8af7099 100644 --- a/providers/crtsh_provider.py +++ b/providers/crtsh_provider.py @@ -293,6 +293,16 @@ class CrtShProvider(BaseProvider): cert_domains = self._extract_domains_from_certificate(cert_data) all_discovered_domains.update(cert_domains) + issuer_name = self._parse_issuer_organization(cert_data.get('issuer_name', '')) + if issuer_name: + result.add_relationship( + source_node=domain, + target_node=issuer_name, + relationship_type='issued_by', + provider=self.name, + confidence=0.95 + ) + for cert_domain in cert_domains: if not _is_valid_domain(cert_domain): continue diff --git a/providers/shodan_provider.py b/providers/shodan_provider.py index 0e7004c..78f2301 100644 --- a/providers/shodan_provider.py +++ b/providers/shodan_provider.py @@ -215,6 +215,27 @@ class ShodanProvider(BaseProvider): """ result = ProviderResult() + isp_name = data.get('org') + asn_value = data.get('asn') + + if isp_name and asn_value: + result.add_relationship( + source_node=ip, + target_node=isp_name, + relationship_type='ip_to_isp', + provider=self.name, + confidence=0.9, + raw_data={'asn': asn_value} + ) + result.add_attribute( + target_node=isp_name, + name='asn', + value=asn_value, + attr_type='isp_info', + provider=self.name, + confidence=0.9 + ) + for key, value in data.items(): if key == 'hostnames': for hostname in value: @@ -235,24 +256,6 @@ class ShodanProvider(BaseProvider): raw_data=data, discovery_method="shodan_host_lookup" ) - elif key == 'asn': - asn_name = f"AS{value[2:]}" if isinstance(value, str) and value.startswith('AS') else f"AS{value}" - result.add_relationship( - source_node=ip, - target_node=asn_name, - relationship_type='shodan_asn_membership', - provider=self.name, - confidence=0.7, - raw_data=data - ) - self.log_relationship_discovery( - source_node=ip, - target_node=asn_name, - relationship_type='shodan_asn_membership', - confidence_score=0.7, - raw_data=data, - discovery_method="shodan_asn_lookup" - ) elif key == 'ports': for port in value: result.add_attribute( diff --git a/static/js/graph.js b/static/js/graph.js index 3a4a839..079eb6b 100644 --- a/static/js/graph.js +++ b/static/js/graph.js @@ -665,7 +665,8 @@ class GraphManager { const colors = { 'domain': '#00ff41', // Green 'ip': '#ff9900', // Amber - 'asn': '#00aaff', // Blue + 'isp': '#00aaff', // Blue + 'ca': '#ff6b6b', // Red 'large_entity': '#ff6b6b', // Red for large entities 'correlation_object': '#9620c0ff' }; @@ -681,7 +682,8 @@ class GraphManager { const borderColors = { 'domain': '#00aa2e', 'ip': '#cc7700', - 'asn': '#0088cc', + 'isp': '#0088cc', + 'ca': '#cc5555', 'correlation_object': '#c235c9ff' }; return borderColors[nodeType] || '#666666'; @@ -696,9 +698,10 @@ class GraphManager { const sizes = { 'domain': 12, 'ip': 14, - 'asn': 16, + 'isp': 16, + 'ca': 16, 'correlation_object': 8, - 'large_entity': 5 + 'large_entity': 25 }; return sizes[nodeType] || 12; } @@ -712,9 +715,10 @@ class GraphManager { const shapes = { 'domain': 'dot', 'ip': 'square', - 'asn': 'triangle', + 'isp': 'triangle', + 'ca': 'diamond', 'correlation_object': 'hexagon', - 'large_entity': 'database' + 'large_entity': 'dot' }; return shapes[nodeType] || 'dot'; } diff --git a/templates/index.html b/templates/index.html index 1f9fad7..0c7a4f9 100644 --- a/templates/index.html +++ b/templates/index.html @@ -137,6 +137,14 @@
IP Addresses +
+
+ ISPs +
+
+
+ Certificate Authorities +
Domain (invalid cert)