@@ -355,7 +355,66 @@ def get_enrichment_data(self, resource: str, resource_type: str, explain: bool =
355
355
raise DemistoException (f'Failed to fetch enrichment data for { resource_type } { resource } : { str (e )} ' )
356
356
357
357
358
-
358
+ def list_ip_information (self , ips : Union [str , List [str ]], explain : bool = False , scan_data : bool = False , sparse : Optional [str ] = None ) -> Dict :
359
+ """
360
+ Fetches information for both IPv4 and IPv6 addresses.
361
+
362
+ Args:
363
+ ips: Either a single IP string or a list of IP strings
364
+ explain: Whether to show details of data used to calculate scores
365
+ scan_data: Whether to include scan data (IPv4 only)
366
+ sparse: Optional specific data to return ('asn', 'asname', or 'sp_risk_score')
367
+
368
+ Returns:
369
+ Dict: Combined results for all IP addresses
370
+ """
371
+ if isinstance (ips , str ):
372
+ ip_list = [ip .strip () for ip in ips .split (',' )]
373
+ else :
374
+ ip_list = ips
375
+
376
+ if len (ip_list ) > 100 :
377
+ raise DemistoException ("Maximum of 100 IPs can be submitted in a single request" )
378
+
379
+ results = []
380
+
381
+ for ip in ip_list :
382
+ try :
383
+ # Determine if IPv4 or IPv6 based on presence of colons
384
+ is_ipv6 = ':' in ip
385
+
386
+ # Build parameters
387
+ params = {
388
+ 'explain' : 1 if explain else 0
389
+ }
390
+ if sparse :
391
+ params ['sparse' ] = sparse
392
+ if not is_ipv6 and scan_data :
393
+ params ['scan_data' ] = 1
394
+
395
+ url_suffix = f"explore/{ 'ipv6' if is_ipv6 else 'ipv4' } /ipv{ 6 if is_ipv6 else 4 } info/{ ip } "
396
+
397
+ response = self ._http_request (
398
+ method = 'GET' ,
399
+ url_suffix = url_suffix ,
400
+ params = params
401
+ )
402
+
403
+ ip_info = response .get ('response' , {}).get ('ip2asn' , [{}])[0 ]
404
+
405
+ ip_info ['ip_type' ] = 'ipv6' if is_ipv6 else 'ipv4'
406
+
407
+ results .append (ip_info )
408
+
409
+ except Exception as e :
410
+ demisto .error (f"Error fetching information for IP { ip } : { str (e )} " )
411
+ results .append ({
412
+ 'ip' : ip ,
413
+ 'ip_type' : 'ipv6' if ':' in ip else 'ipv4' ,
414
+ 'error' : str (e )
415
+ })
416
+
417
+ return {'ips' : results }
359
418
360
419
361
420
@@ -652,6 +711,87 @@ def get_enrichment_data_command(client: Client, args: Dict[str, Any]) -> Command
652
711
except Exception as e :
653
712
demisto .error (f'Failed to get enrichment data: { str (e )} ' )
654
713
raise
714
+
715
+
716
+ def list_ip_information_command (client : Client , args : Dict [str , Any ]) -> CommandResults :
717
+ """
718
+ Command handler for fetching IP information.
719
+
720
+ Args:
721
+ client (Client): The client instance to fetch the data
722
+ args (dict): Command arguments including:
723
+ - ips (str): Comma-separated list of IP addresses
724
+ - explain (bool): Whether to show calculation details
725
+ - scan_data (bool): Whether to include scan data (IPv4 only)
726
+ - sparse (str): Optional specific data to return
727
+
728
+ Returns:
729
+ CommandResults: XSOAR command results
730
+ """
731
+
732
+ ips = args .get ('ips' )
733
+ if not ips :
734
+ raise DemistoException ('No IPs provided. Please provide IPs using the "ips" argument.' )
735
+
736
+ explain = argToBoolean (args .get ('explain' , False ))
737
+ scan_data = argToBoolean (args .get ('scan_data' , False ))
738
+ sparse = args .get ('sparse' )
739
+
740
+ if sparse and sparse not in ['asn' , 'asname' , 'sp_risk_score' ]:
741
+ raise DemistoException ('Invalid sparse value. Must be one of: asn, asname, sp_risk_score' )
742
+
743
+ try :
744
+
745
+ raw_response = client .list_ip_information (ips , explain , scan_data , sparse )
746
+ ip_data = raw_response .get ('ips' , [])
747
+
748
+ markdown = ['### IP Information Results\n ' ]
749
+
750
+ for ip_info in ip_data :
751
+ if 'error' in ip_info :
752
+ markdown .append (f"#### IP: { ip_info .get ('ip' , 'N/A' )} (Error)\n " )
753
+ markdown .append (f"Error: { ip_info ['error' ]} \n " )
754
+ continue
755
+
756
+ markdown .append (f"#### IP: { ip_info .get ('ip' , 'N/A' )} ({ ip_info .get ('ip_type' , 'unknown' ).upper ()} )" )
757
+
758
+ basic_info = {
759
+ 'ASN' : ip_info .get ('asn' , 'N/A' ),
760
+ 'AS Name' : ip_info .get ('asname' , 'N/A' ),
761
+ 'Risk Score' : ip_info .get ('sp_risk_score' , 'N/A' ),
762
+ 'Subnet' : ip_info .get ('subnet' , 'N/A' )
763
+ }
764
+ markdown .append (tableToMarkdown ('Basic Information' , [basic_info ], headers = basic_info .keys ()))
765
+
766
+ if location_info := ip_info .get ('ip_location' , {}):
767
+ location_data = {
768
+ 'Country' : location_info .get ('country_name' , 'N/A' ),
769
+ 'Continent' : location_info .get ('continent_name' , 'N/A' ),
770
+ 'EU Member' : 'Yes' if location_info .get ('country_is_in_european_union' ) else 'No'
771
+ }
772
+ markdown .append (tableToMarkdown ('Location Information' , [location_data ], headers = location_data .keys ()))
773
+
774
+ if ip_info .get ('ip_type' ) == 'ipv4' :
775
+ additional_info = {
776
+ 'PTR Record' : ip_info .get ('ip_ptr' , 'N/A' ),
777
+ 'Is TOR Exit Node' : 'Yes' if ip_info .get ('ip_is_tor_exit_node' ) else 'No' ,
778
+ 'Is DSL/Dynamic' : 'Yes' if ip_info .get ('ip_is_dsl_dynamic' ) else 'No'
779
+ }
780
+ markdown .append (tableToMarkdown ('Additional Information' , [additional_info ], headers = additional_info .keys ()))
781
+
782
+ markdown .append ('\n ' )
783
+
784
+ return CommandResults (
785
+ outputs_prefix = 'SilentPush.IP' ,
786
+ outputs_key_field = 'ip' ,
787
+ outputs = ip_data ,
788
+ readable_output = '\n ' .join (markdown ),
789
+ raw_response = raw_response
790
+ )
791
+
792
+ except Exception as e :
793
+ demisto .error (f"Error in list_ip_information_command: { str (e )} " )
794
+ raise
655
795
656
796
''' MAIN FUNCTION '''
657
797
@@ -694,7 +834,8 @@ def main():
694
834
'silentpush-get-domain-certificates' : get_domain_certificates_command ,
695
835
'silentpush-search-domains' : search_domains_command ,
696
836
'silentpush-list-domain-infratags' : list_domain_infratags_command ,
697
- 'silentpush-get-enrichment-data' : get_enrichment_data_command
837
+ 'silentpush-get-enrichment-data' : get_enrichment_data_command ,
838
+ 'silentpush-list-ip-information' : list_ip_information_command
698
839
}
699
840
700
841
if command in command_handlers :
0 commit comments