APIVoid

APIVoid wraps up a number of services such as ipvoid & urlvoid.

Data Enrichment & Threat Intelligence · APIVoid

Details

IDAPIVoid
ProviderAPIVoid
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

APIVoid wraps up a number of services such as ipvoid & urlvoid.

Configure APIVoid in Cortex

Parameter Description Required
Server URL   True
API Key   False
Suspicious Reputation (Percentage) If the percentage of detections is above this value, the indicator is scored as SUSPICIOUS. If the percentage of detections is below this value, the indicator is scored as GOOD. True
Bad Reputation (Percentage) If the percentage of detections is above this value, the indicator is considered bad. True
Malicious The indicator is considered malicious if the suspicious or bad reputation percentages exceed the thresholds. True
Trust any certificate (not secure)   False
Use system proxy settings   False
Source Reliability Reliability of the source providing the intelligence data. False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

apivoid-ip


Returns the reputation and extended context of the IP.

Base Command

apivoid-ip

Input

Argument Name Description Required
ip The IP to query. Required

Context Output

Path Type Description
IP.Address string IP address
IP.DetectionEngines number The total number of engines that checked the indicator.
IP.Geo unknown  
IP.Geo.Country string The country in which the IP address is located.
IP.Geo.Description string Additional information about the location.
IP.Geo.Location string The geolocation where the IP address is located, in the format: latitude:longitude.
IP.Hostname string The hostname that is mapped to this IP address.
IP.PositiveDetections number The number of engines that positively detected the indicator as malicious.
DBotScore unknown  
DBotScore.Indicator string The indicator that was tested.
DBotScore.Score number The actual score.
DBotScore.Type string The indicator type.
DBotScore.Vendor string The vendor used to calculate the score.
APIVoid.IP.anonymity.is_hosting boolean  
APIVoid.IP.anonymity.is_proxy boolean  
APIVoid.IP.anonymity.is_tor boolean  
APIVoid.IP.anonymity.is_vpn boolean  
APIVoid.IP.anonymity.is_webproxy boolean  
APIVoid.IP.blacklists.detection_rate string  
APIVoid.IP.blacklists.detections number  
APIVoid.IP.blacklists.engines.detected boolean  
APIVoid.IP.blacklists.engines.elapsed string  
APIVoid.IP.blacklists.engines.engine string  
APIVoid.IP.blacklists.engines.reference string  
APIVoid.IP.blacklists.engines_count number  
APIVoid.IP.blacklists.scantime string  
APIVoid.IP.information.isp string  
APIVoid.IP.information.latitude string  
APIVoid.IP.information.reverse_dns string  
APIVoid.IP.information.longitude string  
APIVoid.IP.information.country_calling_code string  
APIVoid.IP.information.country_name string  
APIVoid.IP.information.region_name string  
APIVoid.IP.information.country_code string  
APIVoid.IP.information.continent_name string  
APIVoid.IP.information.continent_code string  
APIVoid.IP.information.country_currency string  
APIVoid.IP.information.city_name string  
APIVoid.IP.ip string  

apivoid-domain


Returns the reputation of the domain.

Base Command

apivoid-domain

Input

Argument Name Description Required
domain The Domain to query. Required

Context Output

Path Type Description
Domain unknown  
Domain.DNS string A list of IP objects resolved by DNS.
Domain.DetectionEngines number The total number of engines that checked the indicator.
Domain.Name string The domain name, for example: “google.com”.
Domain.PositiveDetections number The number of engines that positively detected the indicator as malicious.
DBotScore unknown  
DBotScore.Indicator string The indicator that was tested.
DBotScore.Score number The actual score.
DBotScore.Type string The indicator type.
DBotScore.Vendor string The vendor used to calculate the score.
APIVoid.Domain.alexa_top_100k boolean  
APIVoid.Domain.domain_length number  
APIVoid.Domain.server.isp string  
APIVoid.Domain.server.ip string  
APIVoid.Domain.server.latitude string  
APIVoid.Domain.server.reverse_dns string  
APIVoid.Domain.server.longitude string  
APIVoid.Domain.server.country_name string  
APIVoid.Domain.server.region_name string  
APIVoid.Domain.server.country_code string  
APIVoid.Domain.server.continent_name string  
APIVoid.Domain.server.continent_code string  
APIVoid.Domain.server.city_name string  
APIVoid.Domain.alexa_top_250k boolean  
APIVoid.Domain.alexa_top_10k boolean  
APIVoid.Domain.most_abused_tld boolean  
APIVoid.Domain.host string  
APIVoid.Domain.blacklists.detection_rate string  
APIVoid.Domain.blacklists.detections number  
APIVoid.Domain.blacklists.engines.confidence string  
APIVoid.Domain.blacklists.engines.detected boolean  
APIVoid.Domain.blacklists.engines.elapsed string  
APIVoid.Domain.blacklists.engines.engine string  
APIVoid.Domain.blacklists.engines.reference string  
APIVoid.Domain.blacklists.engines_count number  
APIVoid.Domain.blacklists.scantime string  
APIVoid.Domain.category.is_anonymizer boolean  
APIVoid.Domain.category.is_free_dynamic_dns boolean  
APIVoid.Domain.category.is_free_hosting boolean  
APIVoid.Domain.category.is_url_shortener boolean  

apivoid-url


Returns the reputation of the URL.

Notice: Submitting indicators using this command might make the indicator data publicly available. See the vendor’s documentation for more details.

Base Command

apivoid-url

Input

Argument Name Description Required
url The URL to query. Required

Context Output

Path Type Description
URL unknown  
URL.Data string The URL
URL.DetectionEngines number The total number of engines that checked the indicator.
URL.PositiveDetections number The number of engines that positively detected the indicator as malicious.
DBotScore unknown  
DBotScore.Indicator string The indicator that was tested.
DBotScore.Score number The actual score.
DBotScore.Type string The indicator type.
DBotScore.Vendor string The vendor used to calculate the score.
APIVoid.URL.risk_score.result number  
APIVoid.URL.domain_blacklist.detections number  
APIVoid.URL.domain_blacklist.engines.detected boolean  
APIVoid.URL.domain_blacklist.engines.name string  
APIVoid.URL.domain_blacklist.engines.reference string  
APIVoid.URL.geo_location.countries string  
APIVoid.URL.server_details.isp string  
APIVoid.URL.server_details.ip string  
APIVoid.URL.server_details.latitude string  
APIVoid.URL.server_details.longitude string  
APIVoid.URL.server_details.hostname string  
APIVoid.URL.server_details.country_name string  
APIVoid.URL.server_details.region_name string  
APIVoid.URL.server_details.country_code string  
APIVoid.URL.server_details.continent_name string  
APIVoid.URL.server_details.continent_code string  
APIVoid.URL.server_details.city_name string  
APIVoid.URL.response_headers.server string  
APIVoid.URL.response_headers.code number  
APIVoid.URL.response_headers.content-type string  
APIVoid.URL.response_headers.date date  
APIVoid.URL.response_headers.cache-control string  
APIVoid.URL.response_headers.host-header string  
APIVoid.URL.response_headers.status string  
APIVoid.URL.response_headers.x-redirect-by string  
APIVoid.URL.response_headers.expires date  
APIVoid.URL.response_headers.location string  
APIVoid.URL.response_headers.content-length string  
APIVoid.URL.response_headers.upgrade string  
APIVoid.URL.response_headers.connection string  
APIVoid.URL.security_checks.is_windows_exe_file_on_ipv4 boolean  
APIVoid.URL.security_checks.is_credit_card_form boolean  
APIVoid.URL.security_checks.is_windows_exe_file_on_free_hosting boolean  
APIVoid.URL.security_checks.is_linux_elf_file_on_ipv4 boolean  
APIVoid.URL.security_checks.is_linux_elf_file_on_free_hosting boolean  
APIVoid.URL.security_checks.is_masked_windows_exe_file boolean  
APIVoid.URL.security_checks.is_zip_on_directory_listing boolean  
APIVoid.URL.security_checks.is_masked_linux_elf_file boolean  
APIVoid.URL.security_checks.is_sinkholed_domain boolean  
APIVoid.URL.security_checks.is_suspended_page boolean  
APIVoid.URL.security_checks.is_suspicious_file_extension boolean  
APIVoid.URL.security_checks.is_uncommon_clickable_url boolean  
APIVoid.URL.security_checks.is_suspicious_content boolean  
APIVoid.URL.security_checks.is_risky_geo_location boolean  
APIVoid.URL.security_checks.is_php_on_directory_listing boolean  
APIVoid.URL.security_checks.is_doc_on_directory_listing boolean  
APIVoid.URL.security_checks.is_doc_on_directory_listing boolean  
APIVoid.URL.security_checks.is_empty_page_title boolean  
APIVoid.URL.security_checks.is_login_form boolean  
APIVoid.URL.security_checks.is_robots_noindex boolean  
APIVoid.URL.security_checks.is_suspicious_domain boolean  
APIVoid.URL.security_checks.is_windows_exe_file_on_free_dynamic_dns boolean  
APIVoid.URL.security_checks.is_most_abused_tld boolean  
APIVoid.URL.security_checks.is_linux_elf_file_on_free_dynamic_dns boolean  
APIVoid.URL.security_checks.is_suspicious_url_pattern boolean  
APIVoid.URL.security_checks.is_valid_https boolean  
APIVoid.URL.security_checks.is_exe_on_directory_listing boolean  
APIVoid.URL.security_checks.is_pdf_on_directory_listing boolean  
APIVoid.URL.security_checks.is_host_an_ipv4 boolean  
APIVoid.URL.security_checks.is_domain_blacklisted boolean  
APIVoid.URL.security_checks.is_china_country boolean  
APIVoid.URL.security_checks.is_windows_exe_file boolean  
APIVoid.URL.security_checks.is_masked_file boolean  
APIVoid.URL.security_checks.is_email_address_on_url_query boolean  
APIVoid.URL.security_checks.is_phishing_heuristic boolean  
APIVoid.URL.security_checks.is_non_standard_port boolean  
APIVoid.URL.security_checks.is_linux_elf_file boolean  
APIVoid.URL.security_checks.is_defaced_heuristic boolean  
APIVoid.URL.security_checks.is_directory_listing boolean  
APIVoid.URL.dns_records.mx.records.country_code string  
APIVoid.URL.dns_records.ns.records.country_name string  
APIVoid.URL.dns_records.ns.records.ip string  
APIVoid.URL.dns_records.ns.records.isp string  
APIVoid.URL.dns_records.ns.records.target string  
APIVoid.URL.redirection.external boolean  
APIVoid.URL.redirection.found boolean  
APIVoid.URL.redirection.url string  
APIVoid.URL.url string  
APIVoid.URL.url_parts.host string  
APIVoid.URL.url_parts.host_nowww string  
APIVoid.URL.url_parts.path string  
APIVoid.URL.url_parts.port number  
APIVoid.URL.url_parts.query string  
APIVoid.URL.url_parts.scheme string  
APIVoid.URL.site_category.is_anonymizer boolean  
APIVoid.URL.site_category.is_free_dynamic_dns boolean  
APIVoid.URL.site_category.is_free_hosting boolean  
APIVoid.URL.site_category.is_torrent boolean  
APIVoid.URL.site_category.is_url_shortener boolean  
APIVoid.URL.site_category.is_vpn_provider boolean  
APIVoid.URL.web_page.description string  
APIVoid.URL.web_page.keywords string  
APIVoid.URL.web_page.title string  
APIVoid.URL.html_forms.credit_card_field_present boolean  
APIVoid.URL.html_forms.email_field_present boolean  
APIVoid.URL.html_forms.number_of_total_forms number  
APIVoid.URL.html_forms.number_of_total_input_fields number  
APIVoid.URL.html_forms.password_field_present boolean  
APIVoid.URL.html_forms.two_text_inputs_in_a_form boolean  
APIVoid.URL.file_type.extension string  
APIVoid.URL.file_type.headers string  
APIVoid.URL.file_type.signature string  

ip


Returns the reputation of the IP.

Base Command

ip

Input

Argument Name Description Required
ip The IP to query. Required

Context Output

Path Type Description
IP unknown  
IP.Address string IP address
IP.DetectionEngines number The total number of engines that checked the indicator.
IP.Geo unknown  
IP.Geo.Country string The country in which the IP address is located.
IP.Geo.Description string Additional information about the location.
IP.Geo.Location string The geolocation where the IP address is located, in the format: latitude:longitude.
IP.Hostname string The hostname that is mapped to this IP address.
IP.PositiveDetections number The number of engines that positively detected the indicator as malicious.
DBotScore.Indicator string The indicator that was tested.
DBotScore.Score number The actual score.
DBotScore.Type string The indicator type.
DBotScore.Vendor string The vendor used to calculate the score.

domain


Returns the reputation of the domain.

Base Command

domain

Input

Argument Name Description Required
domain The Domain to query. Required

Context Output

Path Type Description
Domain.DNS string A list of IP objects resolved by DNS.
Domain.DetectionEngines number The total number of engines that checked the indicator.
Domain.Name string The domain name, for example: “google.com”.
Domain.PositiveDetections number The number of engines that positively detected the indicator as malicious.
DBotScore.Indicator string The indicator that was tested.
DBotScore.Score number The actual score.
DBotScore.Type string The indicator type.
DBotScore.Vendor string The vendor used to calculate the score.

url


Returns the reputation of the URL.

Notice: Submitting indicators using this command might make the indicator data publicly available. See the vendor’s documentation for more details.

Base Command

url

Input

Argument Name Description Required
url The URL to query. Required

Context Output

Path Type Description
URL.Data string The URL
URL.DetectionEngines number The total number of engines that checked the indicator.
URL.PositiveDetections number The number of engines that positively detected the indicator as malicious.
URL.RiskScore number  
URL.Score string  
DBotScore unknown  
DBotScore.Indicator string The indicator that was tested.
DBotScore.Score number The actual score.
DBotScore.Type string The indicator type.
DBotScore.Vendor string The vendor used to calculate the score.

apivoid-dns-lookup


Gets DNS records of a host.

Base Command

apivoid-dns-lookup

Input

Argument Name Description Required
host Host to lookup. Required
type The DNS record type to lookup. Possible values are: A, AAAA, MX, NS, DMARK, Reverse, TXT, ANY, CNAME, SOA, SRV, CAA. Default is A. Required

Context Output

Path Type Description
APIVoid.DNS unknown  
APIVoid.DNS.items.host string  
APIVoid.DNS.items.class string  
APIVoid.DNS.items.ttl number  
APIVoid.DNS.items.type string  
APIVoid.DNS.items.ip string  
APIVoid.DNS.found boolean  
APIVoid.DNS.count number  
APIVoid.DNS.items.ipv6 string  
APIVoid.DNS.items.pri number  
APIVoid.DNS.items.target string  
APIVoid.DNS.items.txt string  
APIVoid.DNS.items.entries unknown  
APIVoid.DNS.items.mname string  
APIVoid.DNS.items.rname string  
APIVoid.DNS.items.serial number  
APIVoid.DNS.items.refresh number  
APIVoid.DNS.items.retry number  
APIVoid.DNS.items.expire number  
APIVoid.DNS.items.minimum-ttl number  
APIVoid.DNS.items.weight number  
APIVoid.DNS.items.port number  
APIVoid.DNS.items.flags number  
APIVoid.DNS.items.tag string  
APIVoid.DNS.items.value string  

apivoid-ssl-info


Get useful SSL information from a remote host

Base Command

apivoid-ssl-info

Input

Argument Name Description Required
host Host to lookup SSL. Required

Context Output

Path Type Description
APIVoid.SSL unknown  
APIVoid.SSL.found boolean  
APIVoid.SSL.fingerprint string  
APIVoid.SSL.deprecated_issuer boolean  
APIVoid.SSL.expired boolean  
APIVoid.SSL.valid_peer boolean  
APIVoid.SSL.host string  
APIVoid.SSL.name_match boolean  
APIVoid.SSL.debug_message string  
APIVoid.SSL.blacklisted boolean  
APIVoid.SSL.valid boolean  
APIVoid.SSL.details unknown  
APIVoid.SSL.details.extensions unknown  
APIVoid.SSL.details.extensions.authority_info_access string  
APIVoid.SSL.details.extensions.authority_key_identifier string  
APIVoid.SSL.details.extensions.basic_constraints string  
APIVoid.SSL.details.extensions.certificate_policies string  
APIVoid.SSL.details.extensions.crl_distribution_points string  
APIVoid.SSL.details.extensions.extended_key_usage string  
APIVoid.SSL.details.extensions.key_usage string  
APIVoid.SSL.details.extensions.subject_key_identifier string  
APIVoid.SSL.details.hash string  
APIVoid.SSL.details.issuer unknown  
APIVoid.SSL.details.issuer.common_name string  
APIVoid.SSL.details.issuer.country string  
APIVoid.SSL.details.issuer.location string  
APIVoid.SSL.details.issuer.organization string  
APIVoid.SSL.details.issuer.organization_unit string  
APIVoid.SSL.details.issuer.state string  
APIVoid.SSL.details.signature unknown  
APIVoid.SSL.details.signature.serial string  
APIVoid.SSL.details.signature.serial_hex string  
APIVoid.SSL.details.signature.type string  
APIVoid.SSL.details.subject unknown  
APIVoid.SSL.details.subject.postal_code string  
APIVoid.SSL.details.subject.street string  
APIVoid.SSL.details.subject.name string  
APIVoid.SSL.details.subject.organization_unit string  
APIVoid.SSL.details.subject.state string  
APIVoid.SSL.details.subject.organization string  
APIVoid.SSL.details.subject.location string  
APIVoid.SSL.details.subject.alternative_names string  
APIVoid.SSL.details.subject.country string  
APIVoid.SSL.details.subject.category string  
APIVoid.SSL.details.subject.common_name string  
APIVoid.SSL.details.validity unknown  
APIVoid.SSL.details.validity.days_left number  
APIVoid.SSL.details.validity.valid_from date  
APIVoid.SSL.details.validity.valid_from_timestamp date  
APIVoid.SSL.details.validity.valid_to date  
APIVoid.SSL.details.validity.valid_to_timestamp date  
APIVoid.SSL.details.version string  

apivoid-email-verify


Checks if an email address is disposable, if it has MX records, and more.

Base Command

apivoid-email-verify

Input

Argument Name Description Required
email Email address to lookup. Required

Context Output

Path Type Description
APIVoid.Email unknown  
APIVoid.Email.email string  
APIVoid.Email.valid_format boolean  
APIVoid.Email.username string  
APIVoid.Email.role_address boolean  
APIVoid.Email.suspicious_username boolean  
APIVoid.Email.dirty_words_username boolean  
APIVoid.Email.domain string  
APIVoid.Email.valid_tld boolean  
APIVoid.Email.disposable boolean  
APIVoid.Email.has_mx_records boolean  
APIVoid.Email.free_email boolean  
APIVoid.Email.russian_free_email boolean  
APIVoid.Email.china_free_email boolean  
APIVoid.Email.suspicious_domain boolean  
APIVoid.Email.did_you_mean string  
APIVoid.Email.dirty_words_domain boolean  
APIVoid.Email.domain_popular boolean  
APIVoid.Email.risky_tld boolean  
APIVoid.Email.police_domain boolean  
APIVoid.Email.government_domain boolean  
APIVoid.Email.educational_domain boolean  
APIVoid.Email.should_block boolean  
APIVoid.Email.score number  

apivoid-threatlog


Check if a website is present on ThreatLog database

Base Command

apivoid-threatlog

Input

Argument Name Description Required
host The host the check. Required

Context Output

Path Type Description
APIVoid.ThreatLog.detected boolean  
APIVoid.ThreatLog.host string  
APIVoid.ThreatLog.scantime string  
Domain.Name string The domain name, for example: “google.com”.

apivoid-parked-domain


Detect if a domain (i.e google.com) is actually parked

Base Command

apivoid-parked-domain

Input

Argument Name Description Required
domain Domain to check. Required

Context Output

Path Type Description
APIVoid.ParkedDomain.host string  
APIVoid.ParkedDomain.parked_domain boolean  
Domain.Name string The domain name, for example: “google.com”.

apivoid-domain-age


Get the registration date of a domain and the domain age in days

Base Command

apivoid-domain-age

Input

Argument Name Description Required
domain The domain to check. Required

Context Output

Path Type Description
APIVoid.DomainAge.debug_message string  
APIVoid.DomainAge.domain_age_found boolean  
APIVoid.DomainAge.domain_age_in_days number  
APIVoid.DomainAge.domain_age_in_months number  
APIVoid.DomainAge.domain_age_in_years number  
APIVoid.DomainAge.domain_creation_date string  
APIVoid.DomainAge.domain_registered string  
APIVoid.DomainAge.host string  
Domain.CreationDate date The date that the domain was created.
Domain.Name string The domain name, for example: “google.com”.

apivoid-url-to-image


Capture a high-quality screenshot of any website or URL

Base Command

apivoid-url-to-image

Input

Argument Name Description Required
url URL to capture screenshot of. Required

Context Output

Path Type Description
File.Size number The size of the file.
File.SHA1 string The SHA1 hash of the file.
File.SHA256 string The SHA256 hash of the file.
File.SHA512 string The SHA512 hash of the file.
File.Name string The name of the file.
File.SSDeep string The SSDeep hash of the file.
File.EntryID string The EntryID of the file.
File.Info string Info regarding the file.
File.Type string The type of the file.
File.MD5 string The MD5 hash of the file.
File.Extension string The extension of the file.

apivoid-url-to-pdf


Convert an URL info high-quality and printable PDF document

Base Command

apivoid-url-to-pdf

Input

Argument Name Description Required
url URL to create PDF of. Required

Context Output

Path Type Description
File.Size number The size of the file.
File.SHA1 string The SHA1 hash of the file.
File.SHA256 string The SHA256 hash of the file.
File.SHA512 string The SHA512 hash of the file.
File.Name string The name of the file.
File.SSDeep string The SSDeep hash of the file.
File.EntryID string The EntryID of the file.
File.Info string Info regarding the file.
File.Type string The type of the file.
File.MD5 string The MD5 hash of the file.
File.Extension string The extension of the file.

apivoid-url-to-html


Get the body of an html page after javascript has been executed

Base Command

apivoid-url-to-html

Input

Argument Name Description Required
url URL to create PDF of. Required

Context Output

Path Type Description
File.Size number The size of the file.
File.SHA1 string The SHA1 hash of the file.
File.SHA256 string The SHA256 hash of the file.
File.SHA512 string The SHA512 hash of the file.
File.Name string The name of the file.
File.SSDeep string The SSDeep hash of the file.
File.EntryID string The EntryID of the file.
File.Info string Info regarding the file.
File.Type string The type of the file.
File.MD5 string The MD5 hash of the file.
File.Extension string The extension of the file.

apivoid-site-trustworthiness


A smart API that accurately checks a website’s trustworthiness.

Base Command

apivoid-site-trustworthiness

Input

Argument Name Description Required
host The host to check. Required

Context Output

Path Type Description
APIVoid.SiteTrust.domain_blacklist.detections number  
APIVoid.SiteTrust.domain_blacklist.engines.detected boolean  
APIVoid.SiteTrust.domain_blacklist.engines.name string  
APIVoid.SiteTrust.domain_blacklist.engines.reference string  
APIVoid.SiteTrust.geo_location.countries unknown  
APIVoid.SiteTrust.domain_age.domain_age_in_days number  
APIVoid.SiteTrust.domain_age.domain_age_in_months number  
APIVoid.SiteTrust.domain_age.domain_age_in_years number  
APIVoid.SiteTrust.domain_age.domain_creation_date string  
APIVoid.SiteTrust.domain_age.found boolean  
APIVoid.SiteTrust.server_details.isp string  
APIVoid.SiteTrust.server_details.ip string  
APIVoid.SiteTrust.server_details.latitude number  
APIVoid.SiteTrust.server_details.longitude number  
APIVoid.SiteTrust.server_details.hostname string  
APIVoid.SiteTrust.server_details.country_name string  
APIVoid.SiteTrust.server_details.region_name string  
APIVoid.SiteTrust.server_details.country_code string  
APIVoid.SiteTrust.server_details.continent_name string  
APIVoid.SiteTrust.server_details.continent_code string  
APIVoid.SiteTrust.server_details.city_name string  
APIVoid.SiteTrust.response_headers.server string  
APIVoid.SiteTrust.response_headers.content-encoding string  
APIVoid.SiteTrust.response_headers.code number  
APIVoid.SiteTrust.response_headers.content-type string  
APIVoid.SiteTrust.response_headers.date date  
APIVoid.SiteTrust.response_headers.vary string  
APIVoid.SiteTrust.response_headers.status string  
APIVoid.SiteTrust.response_headers.x-amz-rid string  
APIVoid.SiteTrust.response_headers.content-length string  
APIVoid.SiteTrust.response_headers.connection string  
APIVoid.SiteTrust.host string  
APIVoid.SiteTrust.security_checks.is_website_popular boolean  
APIVoid.SiteTrust.security_checks.is_suspended_site boolean  
APIVoid.SiteTrust.security_checks.is_domain_recent string  
APIVoid.SiteTrust.security_checks.is_heuristic_pattern boolean  
APIVoid.SiteTrust.security_checks.is_sinkholed_domain boolean  
APIVoid.SiteTrust.security_checks.is_risky_geo_location boolean  
APIVoid.SiteTrust.security_checks.is_empty_page_title boolean  
APIVoid.SiteTrust.security_checks.is_robots_noindex boolean  
APIVoid.SiteTrust.security_checks.is_suspicious_domain boolean  
APIVoid.SiteTrust.security_checks.is_most_abused_tld boolean  
APIVoid.SiteTrust.security_checks.is_valid_https boolean  
APIVoid.SiteTrust.security_checks.is_domain_blacklisted boolean  
APIVoid.SiteTrust.security_checks.is_email_configured boolean  
APIVoid.SiteTrust.security_checks.is_china_country boolean  
APIVoid.SiteTrust.security_checks.is_directory_listing boolean  
APIVoid.SiteTrust.security_checks.is_free_email boolean  
APIVoid.SiteTrust.trust_score.result number  
APIVoid.SiteTrust.dns_records unknown  
APIVoid.SiteTrust.redirection.external boolean  
APIVoid.SiteTrust.redirection.found boolean  
APIVoid.SiteTrust.redirection.url string  
APIVoid.SiteTrust.url_parts.host string  
APIVoid.SiteTrust.url_parts.host_nowww string  
APIVoid.SiteTrust.url_parts.path string  
APIVoid.SiteTrust.url_parts.port string  
APIVoid.SiteTrust.url_parts.query string  
APIVoid.SiteTrust.url_parts.scheme string  
APIVoid.SiteTrust.ecommerce_platform.is_magento boolean  
APIVoid.SiteTrust.ecommerce_platform.is_opencart boolean  
APIVoid.SiteTrust.ecommerce_platform.is_prestashop boolean  
APIVoid.SiteTrust.ecommerce_platform.is_shopify boolean  
APIVoid.SiteTrust.ecommerce_platform.is_woocommerce boolean  
APIVoid.SiteTrust.ecommerce_platform.is_zencart boolean  
APIVoid.SiteTrust.web_page.description string  
APIVoid.SiteTrust.web_page.keywords string  
APIVoid.SiteTrust.web_page.title string  
APIVoid.SiteTrust.targeted_brands.patagonia boolean  
APIVoid.SiteTrust.targeted_brands.rolex boolean  
APIVoid.SiteTrust.targeted_brands.timberland boolean  
APIVoid.SiteTrust.targeted_brands.hugoboss boolean  
APIVoid.SiteTrust.targeted_brands.moncler boolean  
APIVoid.SiteTrust.targeted_brands.longchamp boolean  
APIVoid.SiteTrust.targeted_brands.abercrombie boolean  
APIVoid.SiteTrust.targeted_brands.montblanc boolean  
APIVoid.SiteTrust.targeted_brands.carhartt boolean  
APIVoid.SiteTrust.targeted_brands.rayban boolean  
APIVoid.SiteTrust.targeted_brands.drmartens boolean  
APIVoid.SiteTrust.targeted_brands.hermes boolean  
APIVoid.SiteTrust.targeted_brands.oakley boolean  
APIVoid.SiteTrust.targeted_brands.michaelkors boolean  
APIVoid.SiteTrust.targeted_brands.louisvuitton boolean  
APIVoid.SiteTrust.targeted_brands.birkenstock boolean  
APIVoid.SiteTrust.targeted_brands.adidas boolean  
APIVoid.SiteTrust.targeted_brands.vans boolean  
APIVoid.SiteTrust.targeted_brands.ralphlauren boolean  
APIVoid.SiteTrust.targeted_brands.mulberry boolean  
APIVoid.SiteTrust.targeted_brands.converse boolean  
APIVoid.SiteTrust.targeted_brands.versace boolean  
APIVoid.SiteTrust.targeted_brands.ugg boolean  
APIVoid.SiteTrust.targeted_brands.nike boolean  
APIVoid.SiteTrust.targeted_brands.swarovski boolean  
APIVoid.SiteTrust.targeted_brands.peuterey boolean  
APIVoid.SiteTrust.targeted_brands.cartier boolean  
APIVoid.SiteTrust.targeted_brands.pandora boolean  
APIVoid.SiteTrust.targeted_brands.burberry boolean  
APIVoid.SiteTrust.targeted_brands.gucci boolean  
APIVoid.SiteTrust.targeted_brands.salomon boolean  
APIVoid.SiteTrust.targeted_brands.liujo boolean  
APIVoid.SiteTrust.targeted_brands.truereligion boolean  

Configuration parameters

  • url — Server URL (required)
  • credentials
  • apikey — API KEY
  • suspicious — Suspicious Reputation (Percentage) (required)
  • bad — Bad Reputation (Percentage) (required)
  • malicious — Malicious (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • integrationReliability — Source Reliability
  • feedExpirationPolicy
  • feedExpirationInterval

Commands (16)

  • apivoid-dns-lookup

    Gets DNS records of a host.

  • apivoid-domain

    Returns the reputation of the domain.

  • apivoid-domain-age

    Get the registration date of a domain and the domain age in days

  • apivoid-email-verify

    Checks if an email address is disposable, if it has MX records, and more.

  • apivoid-ip

    Returns the reputation and extended context of the IP.

  • apivoid-parked-domain

    Detect if a domain (i.e google.com) is actually parked

  • apivoid-site-trustworthiness

    A smart API that accurately checks a website's trustworthiness.

  • apivoid-ssl-info

    Get useful SSL information from a remote host

  • apivoid-threatlog Deprecated

    Deprecated. This command is deprecated because it is not supported in API V2. Check if a website is present on ThreatLog database.

  • apivoid-url

    Returns the reputation of the URL.

  • apivoid-url-to-html Deprecated

    Deprecated. This command is deprecated because it is not supported in API V2. Get the body of an html page after javascript has been executed.

  • apivoid-url-to-image

    Capture a high-quality screenshot of any website or URL

  • apivoid-url-to-pdf

    Convert an URL info high-quality and printable PDF document

  • domain

    Returns the reputation of the domain.

  • ip

    Returns the reputation of the IP.

  • url

    Returns the reputation of the URL.

"""
Unit tests for APIVoid integration
Tests all commands, helper functions, and error handling scenarios with strict HTTP request validation
"""

import pytest
import json
import os
from CommonServerPython import *
from APIVoid import (
    Client,
    calculate_dbot_score,
    ip_reputation_command,
    domain_reputation_command,
    url_reputation_command,
    dns_lookup_command,
    ssl_info_command,
    email_verify_command,
    parked_domain_command,
    domain_age_command,
    screenshot_command,
    url_to_pdf_command,
    site_trustworthiness_command,
    test_module as module_test,
    main,
)


# ============================================================================
# HELPER FUNCTION TO LOAD MOCK DATA FROM FILES
# ============================================================================


def load_mock_response(filename: str) -> dict:
    """
    Load mock response data from JSON file in test_data directory

    Args:
        filename: Name of the JSON file (e.g., 'ip-reputation-response.json')

    Returns:
        Dictionary containing the mock response data
    """
    test_data_dir = os.path.join(os.path.dirname(__file__), "test_data")
    file_path = os.path.join(test_data_dir, filename)

    with open(file_path) as f:
        return json.load(f)


# ============================================================================
# MOCK DATA - V2 API Responses (loaded from files)
# ============================================================================

MOCK_IP_RESPONSE = load_mock_response("ip-reputation-response.json")
MOCK_DOMAIN_RESPONSE = load_mock_response("domain-reputation-response.json")
MOCK_URL_RESPONSE = load_mock_response("url-reputation-response.json")
MOCK_DNS_RESPONSE = load_mock_response("dns-lookup-response.json")
MOCK_SSL_RESPONSE = load_mock_response("ssl-info-response.json")
MOCK_EMAIL_RESPONSE = load_mock_response("email-verify-response.json")
MOCK_PARKED_DOMAIN_RESPONSE = load_mock_response("parked-domain-response.json")
MOCK_DOMAIN_AGE_RESPONSE = load_mock_response("domain-age-response.json")
MOCK_SCREENSHOT_RESPONSE = load_mock_response("screenshot-response.json")
MOCK_PDF_RESPONSE = load_mock_response("pdf-response.json")
MOCK_SITE_TRUST_RESPONSE = load_mock_response("site-trust-response.json")
MOCK_ERROR_RESPONSE = load_mock_response("error-response.json")


# ============================================================================
# FIXTURES
# ============================================================================


@pytest.fixture
def client():
    """
    Create a test client instance

    Given: Valid API credentials
    When: Client is initialized
    Then: Client should be created with proper headers
    """
    return Client(base_url="https://api.apivoid.com", apikey="test-api-key", verify=False, proxy=False)


@pytest.fixture
def mock_demisto(mocker):
    """
    Mock demisto functions

    Given: Demisto environment
    When: Tests are run
    Then: Demisto functions should be mocked
    """
    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "credentials": {"password": "test-api-key"},
            "insecure": False,
            "proxy": False,
            "integrationReliability": "C - Fairly reliable",
            "suspicious": "30",
            "bad": "60",
        },
    )
    mocker.patch.object(demisto, "args", return_value={})
    mocker.patch.object(demisto, "command", return_value="test-module")
    mocker.patch.object(demisto, "results")
    mocker.patch.object(demisto, "debug")
    return mocker


# ============================================================================
# TEST CLIENT CLASS WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestClientWithStrictValidation:
    """Test Client class with strict HTTP request validation"""

    def test_client_initialization(self, client):
        """
        Test client initialization

        Given: Valid API credentials
        When: Client is initialized
        Then: Headers should contain API key and Content-Type
        """
        assert client._headers["X-API-Key"] == "test-api-key"
        assert client._headers["Content-Type"] == "application/json"
        assert client._base_url == "https://api.apivoid.com"

    def test_api_request_validates_method(self, client, mocker):
        """
        Test API request uses POST method

        Given: A client instance
        When: api_request is called
        Then: Should use POST method
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value={"result": "success"})

        client.api_request("/v2/test", {"param": "value"})

        # Validate exact method
        assert mock_http.call_args[1]["method"] == "POST"

    def test_api_request_validates_url_suffix(self, client, mocker):
        """
        Test API request uses correct URL suffix

        Given: A client instance
        When: api_request is called with endpoint
        Then: Should use exact URL suffix
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value={"result": "success"})

        client.api_request("/v2/ip-reputation", {"ip": "8.8.8.8"})

        # Validate exact URL suffix
        assert mock_http.call_args[1]["url_suffix"] == "/v2/ip-reputation"

    def test_api_request_validates_json_data(self, client, mocker):
        """
        Test API request sends correct JSON data

        Given: A client instance
        When: api_request is called with data
        Then: Should send exact JSON data
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value={"result": "success"})

        test_data = {"ip": "8.8.8.8"}
        client.api_request("/v2/ip-reputation", test_data)

        # Validate exact JSON data
        assert mock_http.call_args[1]["json_data"] == test_data

    def test_api_request_validates_headers(self, client, mocker):
        """
        Test API request includes correct headers

        Given: A client instance
        When: api_request is called
        Then: Should include X-API-Key and Content-Type headers
        """
        mocker.patch.object(client, "_http_request", return_value={"result": "success"})

        client.api_request("/v2/test", {"param": "value"})

        # Validate headers are set in client
        assert client._headers["X-API-Key"] == "test-api-key"
        assert client._headers["Content-Type"] == "application/json"


# ============================================================================
# TEST IP REPUTATION WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestIpReputationWithStrictValidation:
    """Test IP reputation command with strict HTTP request validation"""

    def test_ip_reputation_request_validation(self, client, mocker):
        """
        Test IP reputation sends exact request format

        Given: Valid IP address
        When: ip_reputation_command is called
        Then: Should send POST to /v2/ip-reputation with exact JSON body {"ip": "value"}
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_IP_RESPONSE)

        args = {"ip": "8.8.8.8"}
        thresholds = {"suspicious": 30, "bad": 60}

        ip_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

        # Strict validation of request
        mock_http.assert_called_once()
        call_kwargs = mock_http.call_args[1]
        assert call_kwargs["method"] == "POST"
        assert call_kwargs["url_suffix"] == "/v2/ip-reputation"
        assert call_kwargs["json_data"] == {"ip": "8.8.8.8"}

    def test_ip_reputation_different_ip_addresses(self, client, mocker):
        """
        Test IP reputation with different IP addresses

        Given: Different IP addresses
        When: ip_reputation_command is called
        Then: Should send correct IP in request body
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_IP_RESPONSE)
        thresholds = {"suspicious": 30, "bad": 60}

        test_ips = ["8.8.8.8", "1.1.1.1", "192.168.1.1", "10.0.0.1"]

        for test_ip in test_ips:
            mock_http.reset_mock()
            args = {"ip": test_ip}
            ip_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

            # Validate exact IP in request
            assert mock_http.call_args[1]["json_data"]["ip"] == test_ip

    def test_ip_reputation_success(self, client, mocker):
        """
        Test successful IP reputation check

        Given: Valid IP address
        When: ip_reputation_command is called
        Then: Should return CommandResults with IP indicator
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_IP_RESPONSE)

        args = {"ip": "8.8.8.8"}
        thresholds = {"suspicious": 30, "bad": 60}

        result = ip_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

        assert isinstance(result, CommandResults)
        assert result.indicator.ip == "8.8.8.8"
        assert result.outputs_prefix == "APIVoid.IP"
        assert result.outputs_key_field == "ip"


# ============================================================================
# TEST DOMAIN REPUTATION WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestDomainReputationWithStrictValidation:
    """Test domain reputation command with strict HTTP request validation"""

    def test_domain_reputation_request_validation(self, client, mocker):
        """
        Test domain reputation sends exact request format

        Given: Valid domain
        When: domain_reputation_command is called
        Then: Should send POST to /v2/domain-reputation with exact JSON body {"host": "value"}
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_DOMAIN_RESPONSE)

        args = {"domain": "google.com"}
        thresholds = {"suspicious": 30, "bad": 60}

        domain_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

        # Strict validation of request
        mock_http.assert_called_once()
        call_kwargs = mock_http.call_args[1]
        assert call_kwargs["method"] == "POST"
        assert call_kwargs["url_suffix"] == "/v2/domain-reputation"
        assert call_kwargs["json_data"] == {"host": "google.com"}

    def test_domain_reputation_different_domains(self, client, mocker):
        """
        Test domain reputation with different domains

        Given: Different domains
        When: domain_reputation_command is called
        Then: Should send correct domain in request body
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_DOMAIN_RESPONSE)
        thresholds = {"suspicious": 30, "bad": 60}

        test_domains = ["example.com", "google.com", "test.org", "subdomain.example.com"]

        for test_domain in test_domains:
            mock_http.reset_mock()
            args = {"domain": test_domain}
            domain_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

            # Validate exact domain in request
            assert mock_http.call_args[1]["json_data"]["host"] == test_domain


# ============================================================================
# TEST URL REPUTATION WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestUrlReputationWithStrictValidation:
    """Test URL reputation command with strict HTTP request validation"""

    def test_url_reputation_request_validation(self, client, mocker):
        """
        Test URL reputation sends exact request format

        Given: Valid URL
        When: url_reputation_command is called
        Then: Should send POST to /v2/url-reputation with exact JSON body {"url": "value"}
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_URL_RESPONSE)

        args = {"url": "https://example.com"}
        thresholds = {"suspicious": 30, "bad": 60}

        url_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

        # Strict validation of request
        mock_http.assert_called_once()
        call_kwargs = mock_http.call_args[1]
        assert call_kwargs["method"] == "POST"
        assert call_kwargs["url_suffix"] == "/v2/url-reputation"
        assert call_kwargs["json_data"] == {"url": "https://example.com"}

    def test_url_reputation_different_urls(self, client, mocker):
        """
        Test URL reputation with different URLs

        Given: Different URLs
        When: url_reputation_command is called
        Then: Should send correct URL in request body
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_URL_RESPONSE)
        thresholds = {"suspicious": 30, "bad": 60}

        test_urls = [
            "https://example.com",
            "http://test.org/path",
            "https://www.disney.com?param=value",
        ]

        for test_url in test_urls:
            mock_http.reset_mock()
            args = {"url": test_url}
            url_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

            # Validate exact URL in request
            assert mock_http.call_args[1]["json_data"]["url"] == test_url


# ============================================================================
# TEST DNS LOOKUP WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestDnsLookupWithStrictValidation:
    """Test DNS lookup command with strict HTTP request validation"""

    def test_dns_lookup_request_validation(self, client, mocker):
        """
        Test DNS lookup sends exact request format

        Given: Valid host and DNS type
        When: dns_lookup_command is called
        Then: Should send POST to /v2/dns-lookup with exact JSON body {"host": "value", "dns_types": "type"}
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_DNS_RESPONSE)

        args = {"host": "example.com", "type": "A"}

        dns_lookup_command(client, args)

        # Strict validation of request
        mock_http.assert_called_once()
        call_kwargs = mock_http.call_args[1]
        assert call_kwargs["method"] == "POST"
        assert call_kwargs["url_suffix"] == "/v2/dns-lookup"
        assert call_kwargs["json_data"] == {"host": "example.com", "dns_types": "A"}

    def test_dns_lookup_default_type(self, client, mocker):
        """
        Test DNS lookup with default type

        Given: Host without type specified
        When: dns_lookup_command is called
        Then: Should default to A record in request
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_DNS_RESPONSE)

        args = {"host": "example.com"}

        dns_lookup_command(client, args)

        # Validate default type is A
        assert mock_http.call_args[1]["json_data"]["dns_types"] == "A"

    def test_dns_lookup_different_types(self, client, mocker):
        """
        Test DNS lookup with different DNS types

        Given: Different DNS types
        When: dns_lookup_command is called
        Then: Should send correct dns_types in request
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_DNS_RESPONSE)

        test_types = ["A", "AAAA", "MX", "NS", "TXT", "A,AAAA,MX,NS,TXT,SOA,DMARC,CAA,SRV"]

        for dns_type in test_types:
            mock_http.reset_mock()
            args = {"host": "example.com", "type": dns_type}
            dns_lookup_command(client, args)

            # Validate exact dns_types in request
            assert mock_http.call_args[1]["json_data"]["dns_types"] == dns_type


# ============================================================================
# TEST SSL INFO WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestSslInfoWithStrictValidation:
    """Test SSL info command with strict HTTP request validation"""

    def test_ssl_info_request_validation(self, client, mocker):
        """
        Test SSL info sends exact request format

        Given: Valid host
        When: ssl_info_command is called
        Then: Should send POST to /v2/ssl-info with exact JSON body {"host": "value"}
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_SSL_RESPONSE)

        args = {"host": "paypal.com"}

        ssl_info_command(client, args)

        # Strict validation of request
        mock_http.assert_called_once()
        call_kwargs = mock_http.call_args[1]
        assert call_kwargs["method"] == "POST"
        assert call_kwargs["url_suffix"] == "/v2/ssl-info"
        assert call_kwargs["json_data"] == {"host": "paypal.com"}

    def test_ssl_info_different_hosts(self, client, mocker):
        """
        Test SSL info with different hosts

        Given: Different hosts
        When: ssl_info_command is called
        Then: Should send correct host in request body
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_SSL_RESPONSE)

        test_hosts = ["example.com", "google.com", "github.com"]

        for test_host in test_hosts:
            mock_http.reset_mock()
            args = {"host": test_host}
            ssl_info_command(client, args)

            # Validate exact host in request
            assert mock_http.call_args[1]["json_data"]["host"] == test_host


# ============================================================================
# TEST EMAIL VERIFY WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestEmailVerifyWithStrictValidation:
    """Test email verify command with strict HTTP request validation"""

    def test_email_verify_request_validation(self, client, mocker):
        """
        Test email verify sends exact request format

        Given: Valid email address
        When: email_verify_command is called
        Then: Should send POST to /v2/email-verify with exact JSON body {"email": "value"}
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_EMAIL_RESPONSE)

        args = {"email": "test@test.com"}

        email_verify_command(client, args)

        # Strict validation of request
        mock_http.assert_called_once()
        call_kwargs = mock_http.call_args[1]
        assert call_kwargs["method"] == "POST"
        assert call_kwargs["url_suffix"] == "/v2/email-verify"
        assert call_kwargs["json_data"] == {"email": "test@test.com"}

    def test_email_verify_different_emails(self, client, mocker):
        """
        Test email verify with different email addresses

        Given: Different email addresses
        When: email_verify_command is called
        Then: Should send correct email in request body
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_EMAIL_RESPONSE)

        test_emails = ["test@example.com", "user@domain.org", "admin@company.co.uk"]

        for test_email in test_emails:
            mock_http.reset_mock()
            args = {"email": test_email}
            email_verify_command(client, args)

            # Validate exact email in request
            assert mock_http.call_args[1]["json_data"]["email"] == test_email


# ============================================================================
# TEST PARKED DOMAIN WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestParkedDomainWithStrictValidation:
    """Test parked domain command with strict HTTP request validation"""

    def test_parked_domain_request_validation(self, client, mocker):
        """
        Test parked domain sends exact request format

        Given: Valid domain
        When: parked_domain_command is called
        Then: Should send POST to /v2/parked-domain with exact JSON body {"host": "value"}
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_PARKED_DOMAIN_RESPONSE)

        args = {"domain": "example.com"}

        parked_domain_command(client, args)

        # Strict validation of request
        mock_http.assert_called_once()
        call_kwargs = mock_http.call_args[1]
        assert call_kwargs["method"] == "POST"
        assert call_kwargs["url_suffix"] == "/v2/parked-domain"
        assert call_kwargs["json_data"] == {"host": "example.com"}

    def test_parked_domain_different_domains(self, client, mocker):
        """
        Test parked domain with different domains

        Given: Different domains
        When: parked_domain_command is called
        Then: Should send correct domain in request body
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_PARKED_DOMAIN_RESPONSE)

        test_domains = ["test.com", "example.org", "parked.net"]

        for test_domain in test_domains:
            mock_http.reset_mock()
            args = {"domain": test_domain}
            parked_domain_command(client, args)

            # Validate exact domain in request
            assert mock_http.call_args[1]["json_data"]["host"] == test_domain


# ============================================================================
# TEST DOMAIN AGE WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestDomainAgeWithStrictValidation:
    """Test domain age command with strict HTTP request validation"""

    def test_domain_age_request_validation(self, client, mocker):
        """
        Test domain age sends exact request format

        Given: Valid domain
        When: domain_age_command is called
        Then: Should send POST to /v2/domain-age with exact JSON body {"host": "value"}
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_DOMAIN_AGE_RESPONSE)

        args = {"domain": "example.com"}

        domain_age_command(client, args)

        # Strict validation of request
        mock_http.assert_called_once()
        call_kwargs = mock_http.call_args[1]
        assert call_kwargs["method"] == "POST"
        assert call_kwargs["url_suffix"] == "/v2/domain-age"
        assert call_kwargs["json_data"] == {"host": "example.com"}

    def test_domain_age_different_domains(self, client, mocker):
        """
        Test domain age with different domains

        Given: Different domains
        When: domain_age_command is called
        Then: Should send correct domain in request body
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_DOMAIN_AGE_RESPONSE)

        test_domains = ["google.com", "amazon.com", "old-domain.org"]

        for test_domain in test_domains:
            mock_http.reset_mock()
            args = {"domain": test_domain}
            domain_age_command(client, args)

            # Validate exact domain in request
            assert mock_http.call_args[1]["json_data"]["host"] == test_domain


# ============================================================================
# TEST SCREENSHOT WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestScreenshotWithStrictValidation:
    """Test screenshot command with strict HTTP request validation"""

    def test_screenshot_request_validation(self, client, mocker):
        """
        Test screenshot sends exact request format

        Given: Valid URL
        When: screenshot_command is called
        Then: Should send POST to /v2/screenshot with exact JSON body {"url": "value"}
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_SCREENSHOT_RESPONSE)
        mocker.patch("APIVoid.fileResult", return_value={"Type": 3, "File": "test.png"})

        args = {"url": "https://example.com"}

        screenshot_command(client, args)

        # Strict validation of request
        mock_http.assert_called_once()
        call_kwargs = mock_http.call_args[1]
        assert call_kwargs["method"] == "POST"
        assert call_kwargs["url_suffix"] == "/v2/screenshot"
        assert call_kwargs["json_data"] == {"url": "https://example.com"}


# ============================================================================
# TEST URL TO PDF WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestUrlToPdfWithStrictValidation:
    """Test URL to PDF command with strict HTTP request validation"""

    def test_url_to_pdf_request_validation(self, client, mocker):
        """
        Test URL to PDF sends exact request format

        Given: Valid URL
        When: url_to_pdf_command is called
        Then: Should send POST to /v2/url-to-pdf with exact JSON body {"url": "value"}
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_PDF_RESPONSE)
        mocker.patch("APIVoid.fileResult", return_value={"Type": 3, "File": "test.pdf"})

        args = {"url": "https://example.com"}

        url_to_pdf_command(client, args)

        # Strict validation of request
        mock_http.assert_called_once()
        call_kwargs = mock_http.call_args[1]
        assert call_kwargs["method"] == "POST"
        assert call_kwargs["url_suffix"] == "/v2/url-to-pdf"
        assert call_kwargs["json_data"] == {"url": "https://example.com"}


# ============================================================================
# TEST SITE TRUSTWORTHINESS WITH STRICT REQUEST VALIDATION
# ============================================================================


class TestSiteTrustworthinessWithStrictValidation:
    """Test site trustworthiness command with strict HTTP request validation"""

    def test_site_trust_request_validation(self, client, mocker):
        """
        Test site trustworthiness sends exact request format

        Given: Valid host
        When: site_trustworthiness_command is called
        Then: Should send POST to /v2/site-trust with exact JSON body {"host": "value"}
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_SITE_TRUST_RESPONSE)

        args = {"host": "amazon.com"}

        site_trustworthiness_command(client, args)

        # Strict validation of request
        mock_http.assert_called_once()
        call_kwargs = mock_http.call_args[1]
        assert call_kwargs["method"] == "POST"
        assert call_kwargs["url_suffix"] == "/v2/site-trust"
        assert call_kwargs["json_data"] == {"host": "amazon.com"}

    def test_site_trust_different_hosts(self, client, mocker):
        """
        Test site trustworthiness with different hosts

        Given: Different hosts
        When: site_trustworthiness_command is called
        Then: Should send correct host in request body
        """
        mock_http = mocker.patch.object(client, "_http_request", return_value=MOCK_SITE_TRUST_RESPONSE)

        test_hosts = ["google.com", "microsoft.com", "apple.com"]

        for test_host in test_hosts:
            mock_http.reset_mock()
            args = {"host": test_host}
            site_trustworthiness_command(client, args)

            # Validate exact host in request
            assert mock_http.call_args[1]["json_data"]["host"] == test_host


# ============================================================================
# TEST HELPER FUNCTIONS
# ============================================================================


class TestCalculateDbotScore:
    """Test DBot score calculation logic"""

    def test_score_good_zero_detections(self):
        """
        Test DBot score calculation with zero detections

        Given: 0 detections out of 10 engines_count (0%)
        When: calculate_dbot_score is called
        Then: Score should be GOOD (1)
        """
        score = calculate_dbot_score(engines_count=10, detections=0, thresholds={"suspicious": 30, "bad": 60})
        assert score == Common.DBotScore.GOOD

    def test_score_good_below_suspicious_threshold(self):
        """
        Test DBot score calculation below suspicious threshold (previously 'gap' area)

        Given: 20 detections out of 100 engines_count (20%)
        When: calculate_dbot_score is called with suspicious threshold 30%
        Then: Score should be GOOD (1)
        """
        score = calculate_dbot_score(engines_count=100, detections=20, thresholds={"suspicious": 30, "bad": 60})
        assert score == Common.DBotScore.GOOD

    def test_score_suspicious(self):
        """
        Test DBot score calculation in suspicious range

        Given: 40 detections out of 100 engines_count (40%)
        When: calculate_dbot_score is called with thresholds 30/60
        Then: Score should be SUSPICIOUS (2)
        """
        score = calculate_dbot_score(engines_count=100, detections=40, thresholds={"suspicious": 30, "bad": 60})
        assert score == Common.DBotScore.SUSPICIOUS

    def test_score_bad(self):
        """
        Test DBot score calculation in bad range

        Given: 70 detections out of 100 engines_count (70%)
        When: calculate_dbot_score is called with bad threshold 60%
        Then: Score should be BAD (3)
        """
        score = calculate_dbot_score(engines_count=100, detections=70, thresholds={"suspicious": 30, "bad": 60})
        assert score == Common.DBotScore.BAD

    def test_score_none_zero_engines(self):
        """
        Test DBot score calculation with zero engines_count

        Given: 0 engines_count
        When: calculate_dbot_score is called
        Then: Score should be NONE (0)
        """
        score = calculate_dbot_score(engines_count=0, detections=0, thresholds={"suspicious": 30, "bad": 60})
        assert score == Common.DBotScore.NONE


# ============================================================================
# TEST MALICIOUS DESCRIPTION IN DBOT SCORE
# ============================================================================


class TestMaliciousDescription:
    """Test malicious_description in DBotScore when score is BAD"""

    def test_ip_reputation_bad_score_has_malicious_description(self, client, mocker):
        """
        Test IP reputation with BAD score includes malicious_description

        Given: IP with high detection rate (80%)
        When: ip_reputation_command is called with bad threshold 60%
        Then: DBotScore should have malicious_description with detection rate
        """
        # Create mock response with high detection rate
        mock_response = MOCK_IP_RESPONSE.copy()
        mock_response["blacklists"]["detections"] = 8
        mock_response["blacklists"]["engines_count"] = 10

        mocker.patch.object(client, "_http_request", return_value=mock_response)

        args = {"ip": "8.8.8.8"}
        thresholds = {"suspicious": 30, "bad": 60}

        result = ip_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

        # Verify DBotScore has malicious_description
        assert result.indicator.dbot_score.score == Common.DBotScore.BAD
        assert result.indicator.dbot_score.malicious_description == "Detection rate of 8/10"

    def test_domain_reputation_bad_score_has_malicious_description(self, client, mocker):
        """
        Test domain reputation with BAD score includes malicious_description

        Given: Domain with high detection rate (70%)
        When: domain_reputation_command is called with bad threshold 60%
        Then: DBotScore should have malicious_description with detection rate
        """
        # Create mock response with high detection rate
        mock_response = MOCK_DOMAIN_RESPONSE.copy()
        mock_response["blacklists"]["detections"] = 7
        mock_response["blacklists"]["engines_count"] = 10

        mocker.patch.object(client, "_http_request", return_value=mock_response)

        args = {"domain": "malicious.com"}
        thresholds = {"suspicious": 30, "bad": 60}

        result = domain_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

        # Verify DBotScore has malicious_description
        assert result.indicator.dbot_score.score == Common.DBotScore.BAD
        assert result.indicator.dbot_score.malicious_description == "Detection rate of 7/10"

    def test_url_reputation_bad_score_has_malicious_description(self, client, mocker):
        """
        Test URL reputation with BAD score includes malicious_description

        Given: URL with high detection rate (75%)
        When: url_reputation_command is called with bad threshold 60%
        Then: DBotScore should have malicious_description with detection rate
        """
        # Create mock response with high detection rate
        mock_response = MOCK_URL_RESPONSE.copy()
        mock_response["domain_blacklist"]["detections"] = 15
        mock_response["domain_blacklist"]["engines_count"] = 20

        mocker.patch.object(client, "_http_request", return_value=mock_response)

        args = {"url": "https://example.com"}
        thresholds = {"suspicious": 30, "bad": 60}

        result = url_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

        # Verify DBotScore has malicious_description
        assert result.indicator.dbot_score.score == Common.DBotScore.BAD
        assert result.indicator.dbot_score.malicious_description == "Detection rate of 15/20"

    def test_ip_reputation_good_score_has_malicious_description(self, client, mocker):
        """
        Test IP reputation with GOOD score still includes malicious_description

        Given: IP with low detection rate (5%)
        When: ip_reputation_command is called
        Then: DBotScore should have malicious_description even when score is GOOD
        """
        # Create mock response with low detection rate
        mock_response = MOCK_IP_RESPONSE.copy()
        mock_response["blacklists"]["detections"] = 1
        mock_response["blacklists"]["engines_count"] = 20

        mocker.patch.object(client, "_http_request", return_value=mock_response)

        args = {"ip": "8.8.8.8"}
        thresholds = {"suspicious": 30, "bad": 60}

        result = ip_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

        # Verify DBotScore has malicious_description even for GOOD score
        assert result.indicator.dbot_score.score == Common.DBotScore.GOOD
        assert result.indicator.dbot_score.malicious_description == "Detection rate of 1/20"


# ============================================================================
# TEST ERROR HANDLING
# ============================================================================


class TestErrorHandling:
    """Test error handling scenarios"""

    def test_ip_reputation_api_error(self, client, mocker):
        """
        Test IP reputation with API error

        Given: API returns error response
        When: ip_reputation_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        args = {"ip": "8.8.8.8"}
        thresholds = {"suspicious": 30, "bad": 60}

        with pytest.raises(DemistoException, match="Error checking IP"):
            ip_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

    def test_domain_reputation_api_error(self, client, mocker):
        """
        Test domain reputation with API error

        Given: API returns error response
        When: domain_reputation_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        args = {"domain": "example.com"}
        thresholds = {"suspicious": 30, "bad": 60}

        with pytest.raises(DemistoException, match="Error checking domain"):
            domain_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

    def test_url_reputation_api_error(self, client, mocker):
        """
        Test URL reputation with API error

        Given: API returns error response
        When: url_reputation_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        args = {"url": "https://example.com"}
        thresholds = {"suspicious": 30, "bad": 60}

        with pytest.raises(DemistoException, match="Error checking URL"):
            url_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

    def test_dns_lookup_api_error(self, client, mocker):
        """
        Test DNS lookup with API error

        Given: API returns error response
        When: dns_lookup_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        args = {"host": "example.com", "type": "A"}

        with pytest.raises(DemistoException, match="Error looking up DNS"):
            dns_lookup_command(client, args)

    def test_screenshot_no_data(self, client, mocker):
        """
        Test screenshot with no data returned

        Given: API returns response without base64_file
        When: screenshot_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value={})

        args = {"url": "https://example.com"}

        with pytest.raises(DemistoException, match="No screenshot data returned"):
            screenshot_command(client, args)

    def test_url_to_pdf_no_data(self, client, mocker):
        """
        Test URL to PDF with no data returned

        Given: API returns response without base64_file
        When: url_to_pdf_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value={})

        args = {"url": "https://example.com"}

        with pytest.raises(DemistoException, match="No PDF data returned"):
            url_to_pdf_command(client, args)


# ============================================================================
# TEST MODULE
# ============================================================================


class TestTestModule:
    """Test the test-module command"""

    def test_module_success(self, client, mocker):
        """
        Test successful test-module

        Given: Valid API credentials
        When: test_module is called
        Then: Should return 'ok'
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_IP_RESPONSE)

        result = module_test(client)

        assert result == "ok"

    def test_module_api_error(self, client, mocker):
        """
        Test test-module with API error

        Given: API returns error
        When: test_module is called
        Then: Should return error message
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        result = module_test(client)

        assert "Test Failed" in result

    def test_module_exception(self, client, mocker):
        """
        Test test-module with exception

        Given: API request raises exception
        When: test_module is called
        Then: Should return error message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("Connection error"))

        result = module_test(client)

        assert "Test Failed" in result


# ============================================================================
# TEST MAIN FUNCTION
# ============================================================================


class TestMain:
    """Test main function and command routing"""

    def test_main_test_module(self, mock_demisto, mocker):
        """
        Test main function with test-module command

        Given: test-module command
        When: main is called
        Then: Should call test_module and return results
        """
        mocker.patch.object(demisto, "command", return_value="test-module")
        mock_return_results = mocker.patch("APIVoid.return_results")
        mocker.patch("APIVoid.test_module", return_value="ok")

        main()

        mock_return_results.assert_called_once_with("ok")

    def test_main_ip_command(self, mock_demisto, mocker):
        """
        Test main function with ip command

        Given: ip command
        When: main is called
        Then: Should call ip_reputation_command with reputation_only=True
        """
        mocker.patch.object(demisto, "command", return_value="ip")
        mocker.patch.object(demisto, "args", return_value={"ip": "8.8.8.8"})
        mocker.patch("APIVoid.return_results")
        mock_ip_cmd = mocker.patch("APIVoid.ip_reputation_command", return_value=CommandResults(readable_output="test"))

        main()

        # Verify reputation_only was True
        assert mock_ip_cmd.call_args[0][2] is True

    def test_main_apivoid_ip_command(self, mock_demisto, mocker):
        """
        Test main function with apivoid-ip command

        Given: apivoid-ip command
        When: main is called
        Then: Should call ip_reputation_command with reputation_only=False
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-ip")
        mocker.patch.object(demisto, "args", return_value={"ip": "8.8.8.8"})
        mocker.patch("APIVoid.return_results")
        mock_ip_cmd = mocker.patch("APIVoid.ip_reputation_command", return_value=CommandResults(readable_output="test"))

        main()

        # Verify reputation_only was False
        assert mock_ip_cmd.call_args[0][2] is False

    def test_main_domain_command(self, mock_demisto, mocker):
        """
        Test main function with domain command

        Given: domain command
        When: main is called
        Then: Should call domain_reputation_command with reputation_only=True
        """
        mocker.patch.object(demisto, "command", return_value="domain")
        mocker.patch.object(demisto, "args", return_value={"domain": "example.com"})
        mocker.patch("APIVoid.return_results")
        mock_domain_cmd = mocker.patch("APIVoid.domain_reputation_command", return_value=CommandResults(readable_output="test"))

        main()

        # Verify reputation_only was True
        assert mock_domain_cmd.call_args[0][2] is True

    def test_main_apivoid_domain_command(self, mock_demisto, mocker):
        """
        Test main function with apivoid-domain command

        Given: apivoid-domain command
        When: main is called
        Then: Should call domain_reputation_command with reputation_only=False
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-domain")
        mocker.patch.object(demisto, "args", return_value={"domain": "example.com"})
        mocker.patch("APIVoid.return_results")
        mock_domain_cmd = mocker.patch("APIVoid.domain_reputation_command", return_value=CommandResults(readable_output="test"))

        main()

        # Verify reputation_only was False
        assert mock_domain_cmd.call_args[0][2] is False

    def test_main_url_command(self, mock_demisto, mocker):
        """
        Test main function with url command

        Given: url command
        When: main is called
        Then: Should call url_reputation_command with reputation_only=True
        """
        mocker.patch.object(demisto, "command", return_value="url")
        mocker.patch.object(demisto, "args", return_value={"url": "https://example.com"})
        mocker.patch("APIVoid.return_results")
        mock_url_cmd = mocker.patch("APIVoid.url_reputation_command", return_value=CommandResults(readable_output="test"))

        main()

        # Verify reputation_only was True
        assert mock_url_cmd.call_args[0][2] is True

    def test_main_apivoid_url_command(self, mock_demisto, mocker):
        """
        Test main function with apivoid-url command

        Given: apivoid-url command
        When: main is called
        Then: Should call url_reputation_command with reputation_only=False
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-url")
        mocker.patch.object(demisto, "args", return_value={"url": "https://example.com"})
        mocker.patch("APIVoid.return_results")
        mock_url_cmd = mocker.patch("APIVoid.url_reputation_command", return_value=CommandResults(readable_output="test"))

        main()

        # Verify reputation_only was False
        assert mock_url_cmd.call_args[0][2] is False

    def test_main_dns_lookup_command(self, mock_demisto, mocker):
        """
        Test main function with apivoid-dns-lookup command

        Given: apivoid-dns-lookup command
        When: main is called
        Then: Should call dns_lookup_command
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-dns-lookup")
        mocker.patch.object(demisto, "args", return_value={"host": "example.com"})
        mocker.patch("APIVoid.return_results")
        mock_dns_cmd = mocker.patch("APIVoid.dns_lookup_command", return_value=CommandResults(readable_output="test"))

        main()

        mock_dns_cmd.assert_called_once()

    def test_main_ssl_info_command(self, mock_demisto, mocker):
        """
        Test main function with apivoid-ssl-info command

        Given: apivoid-ssl-info command
        When: main is called
        Then: Should call ssl_info_command
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-ssl-info")
        mocker.patch.object(demisto, "args", return_value={"host": "example.com"})
        mocker.patch("APIVoid.return_results")
        mock_ssl_cmd = mocker.patch("APIVoid.ssl_info_command", return_value=CommandResults(readable_output="test"))

        main()

        mock_ssl_cmd.assert_called_once()

    def test_main_email_verify_command(self, mock_demisto, mocker):
        """
        Test main function with apivoid-email-verify command

        Given: apivoid-email-verify command
        When: main is called
        Then: Should call email_verify_command
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-email-verify")
        mocker.patch.object(demisto, "args", return_value={"email": "test@example.com"})
        mocker.patch("APIVoid.return_results")
        mock_email_cmd = mocker.patch("APIVoid.email_verify_command", return_value=CommandResults(readable_output="test"))

        main()

        mock_email_cmd.assert_called_once()

    def test_main_parked_domain_command(self, mock_demisto, mocker):
        """
        Test main function with apivoid-parked-domain command

        Given: apivoid-parked-domain command
        When: main is called
        Then: Should call parked_domain_command
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-parked-domain")
        mocker.patch.object(demisto, "args", return_value={"domain": "example.com"})
        mocker.patch("APIVoid.return_results")
        mock_parked_cmd = mocker.patch("APIVoid.parked_domain_command", return_value=CommandResults(readable_output="test"))

        main()

        mock_parked_cmd.assert_called_once()

    def test_main_domain_age_command(self, mock_demisto, mocker):
        """
        Test main function with apivoid-domain-age command

        Given: apivoid-domain-age command
        When: main is called
        Then: Should call domain_age_command
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-domain-age")
        mocker.patch.object(demisto, "args", return_value={"domain": "example.com"})
        mocker.patch("APIVoid.return_results")
        mock_age_cmd = mocker.patch("APIVoid.domain_age_command", return_value=CommandResults(readable_output="test"))

        main()

        mock_age_cmd.assert_called_once()

    def test_main_screenshot_command(self, mock_demisto, mocker):
        """
        Test main function with apivoid-url-to-image command

        Given: apivoid-url-to-image command
        When: main is called
        Then: Should call screenshot_command
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-url-to-image")
        mocker.patch.object(demisto, "args", return_value={"url": "https://example.com"})
        mocker.patch("APIVoid.return_results")
        mock_screenshot_cmd = mocker.patch("APIVoid.screenshot_command", return_value={"Type": 3, "File": "test.png"})

        main()

        mock_screenshot_cmd.assert_called_once()

    def test_main_url_to_pdf_command(self, mock_demisto, mocker):
        """
        Test main function with apivoid-url-to-pdf command

        Given: apivoid-url-to-pdf command
        When: main is called
        Then: Should call url_to_pdf_command
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-url-to-pdf")
        mocker.patch.object(demisto, "args", return_value={"url": "https://example.com"})
        mocker.patch("APIVoid.return_results")
        mock_pdf_cmd = mocker.patch("APIVoid.url_to_pdf_command", return_value={"Type": 3, "File": "test.pdf"})

        main()

        mock_pdf_cmd.assert_called_once()

    def test_main_site_trustworthiness_command(self, mock_demisto, mocker):
        """
        Test main function with apivoid-site-trustworthiness command

        Given: apivoid-site-trustworthiness command
        When: main is called
        Then: Should call site_trustworthiness_command
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-site-trustworthiness")
        mocker.patch.object(demisto, "args", return_value={"host": "example.com"})
        mocker.patch("APIVoid.return_results")
        mock_trust_cmd = mocker.patch("APIVoid.site_trustworthiness_command", return_value=CommandResults(readable_output="test"))

        main()

        mock_trust_cmd.assert_called_once()

    def test_main_deprecated_command(self, mock_demisto, mocker):
        """
        Test main function with deprecated command

        Given: apivoid-threatlog command (deprecated)
        When: main is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(demisto, "command", return_value="apivoid-threatlog")
        mocker.patch.object(demisto, "args", return_value={})
        mock_return_error = mocker.patch("APIVoid.return_error")

        main()

        mock_return_error.assert_called_once()
        assert "is not supported in API V2" in mock_return_error.call_args[0][0]

    def test_main_not_implemented_command(self, mock_demisto, mocker):
        """
        Test main function with not implemented command

        Given: unknown-command
        When: main is called
        Then: Should raise NotImplementedError
        """
        mocker.patch.object(demisto, "command", return_value="unknown-command")
        mocker.patch.object(demisto, "args", return_value={})
        mock_return_error = mocker.patch("APIVoid.return_error")

        main()

        mock_return_error.assert_called_once()
        assert "not implemented" in mock_return_error.call_args[0][0].lower()


# ============================================================================
# TEST EXCEPTION HANDLING IN COMMANDS
# ============================================================================


class TestExceptionHandling:
    """Test exception handling in all commands"""

    def test_ip_reputation_http_exception(self, client, mocker):
        """
        Test IP reputation with HTTP exception

        Given: HTTP request raises exception
        When: ip_reputation_command is called
        Then: Should raise DemistoException with proper message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("Connection timeout"))

        args = {"ip": "8.8.8.8"}
        thresholds = {"suspicious": 30, "bad": 60}

        with pytest.raises(DemistoException, match="Failed to get IP reputation"):
            ip_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

    def test_domain_reputation_http_exception(self, client, mocker):
        """
        Test domain reputation with HTTP exception

        Given: HTTP request raises exception
        When: domain_reputation_command is called
        Then: Should raise DemistoException with proper message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("Network error"))

        args = {"domain": "example.com"}
        thresholds = {"suspicious": 30, "bad": 60}

        with pytest.raises(DemistoException, match="Failed to get domain reputation"):
            domain_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

    def test_url_reputation_http_exception(self, client, mocker):
        """
        Test URL reputation with HTTP exception

        Given: HTTP request raises exception
        When: url_reputation_command is called
        Then: Should raise DemistoException with proper message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("Timeout"))

        args = {"url": "https://example.com"}
        thresholds = {"suspicious": 30, "bad": 60}

        with pytest.raises(DemistoException, match="Failed to get URL reputation"):
            url_reputation_command(client, args, False, thresholds, "C - Fairly reliable")

    def test_dns_lookup_http_exception(self, client, mocker):
        """
        Test DNS lookup with HTTP exception

        Given: HTTP request raises exception
        When: dns_lookup_command is called
        Then: Should raise DemistoException with proper message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("DNS error"))

        args = {"host": "example.com", "type": "A"}

        with pytest.raises(DemistoException, match="Failed to get DNS records"):
            dns_lookup_command(client, args)

    def test_ssl_info_http_exception(self, client, mocker):
        """
        Test SSL info with HTTP exception

        Given: HTTP request raises exception
        When: ssl_info_command is called
        Then: Should raise DemistoException with proper message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("SSL error"))

        args = {"host": "example.com"}

        with pytest.raises(DemistoException, match="Failed to get SSL info"):
            ssl_info_command(client, args)

    def test_email_verify_http_exception(self, client, mocker):
        """
        Test email verify with HTTP exception

        Given: HTTP request raises exception
        When: email_verify_command is called
        Then: Should raise DemistoException with proper message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("Email error"))

        args = {"email": "test@example.com"}

        with pytest.raises(DemistoException, match="Failed to verify email"):
            email_verify_command(client, args)

    def test_parked_domain_http_exception(self, client, mocker):
        """
        Test parked domain with HTTP exception

        Given: HTTP request raises exception
        When: parked_domain_command is called
        Then: Should raise DemistoException with proper message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("Parked error"))

        args = {"domain": "example.com"}

        with pytest.raises(DemistoException, match="Failed to check parked domain"):
            parked_domain_command(client, args)

    def test_domain_age_http_exception(self, client, mocker):
        """
        Test domain age with HTTP exception

        Given: HTTP request raises exception
        When: domain_age_command is called
        Then: Should raise DemistoException with proper message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("Age error"))

        args = {"domain": "example.com"}

        with pytest.raises(DemistoException, match="Failed to get domain age"):
            domain_age_command(client, args)

    def test_screenshot_http_exception(self, client, mocker):
        """
        Test screenshot with HTTP exception

        Given: HTTP request raises exception
        When: screenshot_command is called
        Then: Should raise DemistoException with proper message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("Screenshot error"))

        args = {"url": "https://example.com"}

        with pytest.raises(DemistoException, match="Failed to capture screenshot"):
            screenshot_command(client, args)

    def test_url_to_pdf_http_exception(self, client, mocker):
        """
        Test URL to PDF with HTTP exception

        Given: HTTP request raises exception
        When: url_to_pdf_command is called
        Then: Should raise DemistoException with proper message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("PDF error"))

        args = {"url": "https://example.com"}

        with pytest.raises(DemistoException, match="Failed to convert URL to PDF"):
            url_to_pdf_command(client, args)

    def test_site_trustworthiness_http_exception(self, client, mocker):
        """
        Test site trustworthiness with HTTP exception

        Given: HTTP request raises exception
        When: site_trustworthiness_command is called
        Then: Should raise DemistoException with proper message
        """
        mocker.patch.object(client, "_http_request", side_effect=Exception("Trust error"))

        args = {"host": "example.com"}

        with pytest.raises(DemistoException, match="Failed to get site trustworthiness"):
            site_trustworthiness_command(client, args)


# ============================================================================
# TEST EDGE CASES AND EMPTY RESPONSES
# ============================================================================


class TestEdgeCases:
    """Test edge cases and empty response handling"""

    def test_dns_lookup_no_records(self, client, mocker):
        """
        Test DNS lookup with no records

        Given: API returns empty records
        When: dns_lookup_command is called
        Then: Should return CommandResults with appropriate message
        """
        mocker.patch.object(client, "_http_request", return_value={"host": "example.com", "records": {}})

        args = {"host": "example.com", "type": "A"}

        result = dns_lookup_command(client, args)

        assert isinstance(result, CommandResults)
        assert "No DNS records found" in result.readable_output

    def test_dns_lookup_no_matching_type(self, client, mocker):
        """
        Test DNS lookup with no matching record type

        Given: API returns records but not the requested type
        When: dns_lookup_command is called
        Then: Should return CommandResults with appropriate message
        """
        mocker.patch.object(
            client, "_http_request", return_value={"host": "example.com", "records": {"mx": [{"target": "mail.example.com"}]}}
        )

        args = {"host": "example.com", "type": "A"}

        result = dns_lookup_command(client, args)

        assert isinstance(result, CommandResults)
        assert "No A records found" in result.readable_output

    def test_ssl_info_no_certificate(self, client, mocker):
        """
        Test SSL info with no certificate

        Given: API returns empty certificate
        When: ssl_info_command is called
        Then: Should return CommandResults with appropriate message
        """
        mocker.patch.object(client, "_http_request", return_value={"host": "example.com", "certificate": {}})

        args = {"host": "example.com"}

        result = ssl_info_command(client, args)

        assert isinstance(result, CommandResults)
        assert "No SSL information" in result.readable_output

    def test_ssl_info_api_error(self, client, mocker):
        """
        Test SSL info with API error

        Given: API returns error response
        When: ssl_info_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        args = {"host": "example.com"}

        with pytest.raises(DemistoException, match="Error getting SSL info"):
            ssl_info_command(client, args)

    def test_email_verify_empty_response(self, client, mocker):
        """
        Test email verify with empty response

        Given: API returns empty response
        When: email_verify_command is called
        Then: Should return CommandResults with appropriate message
        """
        mocker.patch.object(client, "_http_request", return_value={})

        args = {"email": "test@example.com"}

        result = email_verify_command(client, args)

        assert isinstance(result, CommandResults)
        assert "No information" in result.readable_output

    def test_email_verify_api_error(self, client, mocker):
        """
        Test email verify with API error

        Given: API returns error response
        When: email_verify_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        args = {"email": "test@example.com"}

        with pytest.raises(DemistoException, match="Error verifying email"):
            email_verify_command(client, args)

    def test_parked_domain_empty_response(self, client, mocker):
        """
        Test parked domain with empty response

        Given: API returns empty response
        When: parked_domain_command is called
        Then: Should return CommandResults with appropriate message
        """
        mocker.patch.object(client, "_http_request", return_value={})

        args = {"domain": "example.com"}

        result = parked_domain_command(client, args)

        assert isinstance(result, CommandResults)
        assert "No information" in result.readable_output

    def test_parked_domain_api_error(self, client, mocker):
        """
        Test parked domain with API error

        Given: API returns error response
        When: parked_domain_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        args = {"domain": "example.com"}

        with pytest.raises(DemistoException, match="Error checking parked domain"):
            parked_domain_command(client, args)

    def test_domain_age_empty_response(self, client, mocker):
        """
        Test domain age with empty response

        Given: API returns empty response
        When: domain_age_command is called
        Then: Should return CommandResults with appropriate message
        """
        mocker.patch.object(client, "_http_request", return_value={})

        args = {"domain": "example.com"}

        result = domain_age_command(client, args)

        assert isinstance(result, CommandResults)
        assert "No information" in result.readable_output

    def test_domain_age_api_error(self, client, mocker):
        """
        Test domain age with API error

        Given: API returns error response
        When: domain_age_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        args = {"domain": "example.com"}

        with pytest.raises(DemistoException, match="Error getting domain age"):
            domain_age_command(client, args)

    def test_screenshot_api_error(self, client, mocker):
        """
        Test screenshot with API error

        Given: API returns error response
        When: screenshot_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        args = {"url": "https://example.com"}

        with pytest.raises(DemistoException, match="Error capturing screenshot"):
            screenshot_command(client, args)

    def test_url_to_pdf_api_error(self, client, mocker):
        """
        Test URL to PDF with API error

        Given: API returns error response
        When: url_to_pdf_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        args = {"url": "https://example.com"}

        with pytest.raises(DemistoException, match="Error converting URL to PDF"):
            url_to_pdf_command(client, args)

    def test_site_trustworthiness_empty_response(self, client, mocker):
        """
        Test site trustworthiness with empty response

        Given: API returns empty response
        When: site_trustworthiness_command is called
        Then: Should return CommandResults with appropriate message
        """
        mocker.patch.object(client, "_http_request", return_value={})

        args = {"host": "example.com"}

        result = site_trustworthiness_command(client, args)

        assert isinstance(result, CommandResults)
        assert "No information" in result.readable_output

    def test_site_trustworthiness_api_error(self, client, mocker):
        """
        Test site trustworthiness with API error

        Given: API returns error response
        When: site_trustworthiness_command is called
        Then: Should raise DemistoException
        """
        mocker.patch.object(client, "_http_request", return_value=MOCK_ERROR_RESPONSE)

        args = {"host": "example.com"}

        with pytest.raises(DemistoException, match="Error getting site trustworthiness"):
            site_trustworthiness_command(client, args)