GreyNoise

GreyNoise is a cybersecurity platform that collects and analyzes Internet-wide scan and attack traffic. With this integration, users can contextualize existing alerts, filter false-positives, identify compromised devices, and track emerging threats.

Data Enrichment & Threat Intelligence · GreyNoise

Details

IDGreyNoise
ProviderGreyNoise
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/greynoise:1.0.0.10926121
Supported ModulesAgentix XSIAM

README

GreyNoise is a cybersecurity platform that collects and analyzes Internet-wide scan and attack traffic. With this integration, users can contextualize existing alerts, filter false-positives, identify compromised devices, and track emerging threats.
This integration was integrated and tested with version 3.0.0 of the GreyNoise SDK.
Supported Cortex XSOAR versions: 6.0.0 and later.

Configure GreyNoise in Cortex

Parameter Description Required
apikey API Key False
insecure Trust any certificate (not secure) False
proxy Use system proxy settings 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.

ip


Runs reputation on IPs.

Base Command

ip

Input

Argument Name Description Required
ip A comma-separated list of IPs. Required

Context Output

Path Type Description
DBotScore.Indicator String The indicator that was tested.
DBotScore.Reliability String The reliability of the data.
DBotScore.Score Number The actual score.
DBotScore.Type String The indicator type.
DBotScore.Vendor String The vendor used to calculate the score.
GreyNoise.IP.address string The IP address of the scanning device IP.
GreyNoise.IP.actor string The overt actor the device has been associated with.
GreyNoise.IP.bot Boolean Whether the IP is associated with known bot activity or not. Common examples include credential stuffing, content scraping, or brute force attacks.
GreyNoise.IP.classification string Whether the device has been categorized as unknown, benign, or malicious.
GreyNoise.IP.cve array CVEs associated with IP.
GreyNoise.IP.first_seen date The date the device was first observed by GreyNoise. Format is ISO8601.
GreyNoise.IP.found boolean Whether the IP was found in GreyNoise records.
GreyNoise.IP.last_seen date The date the device was last observed by GreyNoise. Format is ISO8601.
GreyNoise.IP.last_seen_timestamp string The timestamp when the device was last observed by GreyNoise.
GreyNoise.IP.metadata.asn string The autonomous system identification number.
GreyNoise.IP.metadata.carrier string The carrier information for the IP address.
GreyNoise.IP.metadata.category string Whether the device belongs to a business, isp, hosting, education, or mobile network.
GreyNoise.IP.metadata.city string The city the device is geographically located in.
GreyNoise.IP.metadata.country string The full name of the country.
GreyNoise.IP.metadata.country_code string The two-character country code of the country.
GreyNoise.IP.metadata.datacenter string The datacenter information for the IP address.
GreyNoise.IP.metadata.destination_asns array The list of ASNs targeted by scanning.
GreyNoise.IP.metadata.destination_cities array The list of cities targeted by scanning.
GreyNoise.IP.metadata.destination_countries array The list of countries targeted by scanning.
GreyNoise.IP.metadata.destination_country_codes array The list of country codes targeted by scanning.
GreyNoise.IP.metadata.domain string The domain associated with the IP address.
GreyNoise.IP.metadata.latitude number The latitude coordinate of the IP address location.
GreyNoise.IP.metadata.longitude number The longitude coordinate of the IP address location.
GreyNoise.IP.metadata.mobile boolean Whether the device is on a mobile network.
GreyNoise.IP.metadata.organization string The organization that owns the network that the IP address belongs to.
GreyNoise.IP.metadata.os string The name of the operating system of the device.
GreyNoise.IP.metadata.rdns string Reverse DNS lookup of the IP address.
GreyNoise.IP.metadata.rdns_parent string The parent domain of the reverse DNS lookup.
GreyNoise.IP.metadata.rdns_validated boolean Whether the reverse DNS lookup has been validated.
GreyNoise.IP.metadata.region string The full name of the region the device is geographically located in.
GreyNoise.IP.metadata.sensor_count number The number of sensors that observed activity from this IP.
GreyNoise.IP.metadata.sensor_hits number The number of sensors events recorded from this IP.
GreyNoise.IP.metadata.single_destination boolean Whether the IP targets a single destination.
GreyNoise.IP.metadata.source_city string The city where the IP is geographically located.
GreyNoise.IP.metadata.source_country string The full name of the IP source country.
GreyNoise.IP.metadata.source_country_code string The country code of the IP source country.
GreyNoise.IP.metadata.tor boolean Whether the device is a known Tor exit node.
GreyNoise.IP.tor boolean Whether the device is a known Tor exit node.
GreyNoise.IP.raw_data.hassh.fingerprint string HASSH hash fingerprint string.
GreyNoise.IP.raw_data.hassh.port number TCP port connection where the HASSH hash was identified.
GreyNoise.IP.raw_data.http.md5 array MD5 hashes of HTTP requests made by the device.
GreyNoise.IP.raw_data.http.method array HTTP methods used by the device.
GreyNoise.IP.raw_data.http.path array HTTP paths the device has been observed accessing.
GreyNoise.IP.raw_data.http.request_header array HTTP request headers used by the device.
GreyNoise.IP.raw_data.http.useragent array HTTP user-agents the device has been observed using.
GreyNoise.IP.raw_data.ja3.fingerprint string The JA3 TLS/SSL fingerprint.
GreyNoise.IP.raw_data.ja3.port number The corresponding TCP port for the given JA3 fingerprint.
GreyNoise.IP.raw_data.tls.ja4 array JA4 TLS/SSL fingerprints.
GreyNoise.IP.raw_data.scan.port number The port number(s) the devices has been observed scanning.
GreyNoise.IP.raw_data.scan.protocol string The protocol of the port the device has been observed scanning.
GreyNoise.IP.raw_data.source.bytes number The number of bytes sent by the source.
GreyNoise.IP.raw_data.tls.cipher array TLS cipher suites used by the device.
GreyNoise.IP.raw_data.web.paths array Any HTTP paths the device has been observed crawling the Internet for.
GreyNoise.IP.raw_data.web.useragents array Any HTTP user-agents the device has been observed using while crawling the Internet.
GreyNoise.IP.seen boolean Whether the IP is in record with GreyNoise.
GreyNoise.IP.spoofable boolean Whether the ip is spoofable.
GreyNoise.IP.tags.category string The category of the given tag.
GreyNoise.IP.tags.created date The date the tag was added to the GreyNoise system.
GreyNoise.IP.tags.description string A description of what the tag identifies.
GreyNoise.IP.tags.id string The unique id of the tag.
GreyNoise.IP.tags.intention string The intention of the associated activity the tag identifies.
GreyNoise.IP.tags.name string The name of the tag.
GreyNoise.IP.tags.recommend_block boolean Indicates if IPs associated with this tag should be blocked.
GreyNoise.IP.tags.references string A list of references used to create the tag.
GreyNoise.IP.tags.slug string The unique slug of the tag.
GreyNoise.IP.tags.updated_at date The date the tag was last updated.
GreyNoise.IP.vpn boolean Whether the device is a VPN endpoint or not.
GreyNoise.IP.vpn_service string The name of the VPN service provider of the device.
GreyNoise.IP.category string The category of the business service.
GreyNoise.IP.description string Description of the business service.
GreyNoise.IP.explanation string Explanation of why the IP is considered a business service.
GreyNoise.IP.riot boolean Whether the IP is a common business service.
GreyNoise.IP.last_updated date When was the last time the business service information was updated.
GreyNoise.IP.name string The name of the business service.
GreyNoise.IP.reference string Reference link for the business service.
GreyNoise.IP.trust_level string If the IP is a business service, how trustworthy is the IP.
IP.Address string IP address.
IP.ASN string The autonomous system name for the IP address.
IP.Geo.Country string The country in which the IP address is located.
IP.Geo.Description string Additional information about the location such as city and region.
IP.Hostname string The hostname that is mapped to IP address.
IP.Malicious.Description string A description explaining why the IP address was reported as malicious.
IP.Malicious.Vendor string The vendor reporting the IP address as malicious.

Command Example

!ip ip="64.39.108.148"

IP: 64.39.108.148 found with Reputation: Good

GreyNoise Internet Scanner Intelligence Lookup

IP Internet Scanner Classification Actor Tags Spoofable VPN BOT Tor First Seen Last Seen Timestamp
64.39.108.148 true benign Qualys Qualys (benign - actor) true false false false 2025-05-25 2025-05-25 09:28:51

IP: 64.39.108.148 found with Reputation: Good

Belongs to Common Business Service: Qualys

GreyNoise Business Service Intelligence Lookup

IP Business Service Category Name Trust Level Description Last Updated
64.39.108.148 true vulnerability_management Qualys 1 - Reasonably Ignore Qualys Inc (Qualys) is a provider of cloud-based platform information security and compliance cloud solutions. The company’s cloud platform offers private cloud platforms, private cloud platform appliances, public cloud integrations, and cloud agents. 2025-06-26T13:10:55Z

greynoise-ip-quick-check


Check whether a given IP address is “Internet background noise”, or has been observed scanning or attacking devices across the Internet. Note: It checks against the last 60 days of Internet scanner data.

Base Command

greynoise-ip-quick-check

Input

Argument Name Description Required
ip List IP addresses to retrieve quick check about. Required

Context Output

Path Type Description
GreyNoise.IP.address string The IP address of the scanning device IP.
GreyNoise.IP.internet_scanner_intelligence.found boolean Whether the IP has been observed scanning the internet.
GreyNoise.IP.business_service_intelligence.found boolean Whether the IP is a common business service.
GreyNoise.IP.internet_scanner_intelligence.classification string If the IP has been observed, what is the GreyNoise classification.
GreyNoise.IP.business_service_intelligence.trust_level string If the IP is a business service, how trustworthy is the IP.

Command Example

!greynoise-ip-quick-check ip="45.83.65.120,45.83.66.18"

Human Readable Output

GreyNoise Quick IP Lookup Details

IP Internet Scanner Classification Business Service Trust Level
64.39.108.148 true benign true 1

greynoise-query


Get the information of IP based on the providence filters.

Base Command

greynoise-query

Input

Argument Name Description Required
last_seen The date the device was most recently observed by GreyNoise. Example: 1d, 2d, 12h, or 1m. Optional
organization The organization that owns the network the IP address belongs to. Optional
classification Classification of the device. Possible values: unknown, benign, malicious. Possible values are: unknown, benign, malicious. Optional
spoofable Whether the IP is spoofable or not. Possible values are: true, false. Default is false. Optional
actor The actor the device has been associated with. Optional
cve A CVE to get scanning data about, example CVE-2021-12345. Optional
size Maximum amount of results to grab. Default is 10. Optional
advanced_query GNQL query to filter records.
Note: It merges other arguments and takes higher precedence over the same argument if supplied.
Example:
malicious,
spoofable:false SSH Scanner,
spoofable:false classification:benign tags:POP3 Scanner cve:CVE-2010-0103.
Optional
next_token Scroll token to paginate through results. Optional

Context Output

Path Type Description
GreyNoise.IP.address string The IP address of the scanning device IP.
GreyNoise.IP.business_service_intelligence.category string The category of the business service.
GreyNoise.IP.business_service_intelligence.description string Description of the business service.
GreyNoise.IP.business_service_intelligence.explanation string Explanation of why the IP is considered a business service.
GreyNoise.IP.business_service_intelligence.found boolean Whether the IP is a common business service.
GreyNoise.IP.business_service_intelligence.last_updated date When was the last time the business service information was updated.
GreyNoise.IP.business_service_intelligence.name string The name of the business service.
GreyNoise.IP.business_service_intelligence.reference string Reference link for the business service.
GreyNoise.IP.business_service_intelligence.trust_level string If the IP is a business service, how trustworthy is the IP.
GreyNoise.IP.internet_scanner_intelligence.actor string The overt actor the device has been associated with.
GreyNoise.IP.internet_scanner_intelligence.bot Boolean Whether the IP is associated with known bot activity or not. Common examples include credential stuffing, content scraping, or brute force attacks.
GreyNoise.IP.internet_scanner_intelligence.classification string Whether the device has been categorized as unknown, benign, or malicious.
GreyNoise.IP.internet_scanner_intelligence.cve array CVEs associated with IP.
GreyNoise.IP.internet_scanner_intelligence.first_seen date The date the device was first observed by GreyNoise. Format is ISO8601.
GreyNoise.IP.internet_scanner_intelligence.found boolean Whether the IP was found in GreyNoise records.
GreyNoise.IP.internet_scanner_intelligence.last_seen date The date the device was last observed by GreyNoise. Format is ISO8601.
GreyNoise.IP.internet_scanner_intelligence.last_seen_timestamp string The timestamp when the device was last observed by GreyNoise.
GreyNoise.IP.internet_scanner_intelligence.metadata.asn string The autonomous system identification number.
GreyNoise.IP.internet_scanner_intelligence.metadata.carrier string The carrier information for the IP address.
GreyNoise.IP.internet_scanner_intelligence.metadata.category string Whether the device belongs to a business, isp, hosting, education, or mobile network.
GreyNoise.IP.internet_scanner_intelligence.metadata.city string The city the device is geographically located in.
GreyNoise.IP.internet_scanner_intelligence.metadata.country string The full name of the country.
GreyNoise.IP.internet_scanner_intelligence.metadata.country_code string The two-character country code of the country.
GreyNoise.IP.internet_scanner_intelligence.metadata.datacenter string The datacenter information for the IP address.
GreyNoise.IP.internet_scanner_intelligence.metadata.destination_asns array The list of ASNs targeted by scanning.
GreyNoise.IP.internet_scanner_intelligence.metadata.destination_cities array The list of cities targeted by scanning.
GreyNoise.IP.internet_scanner_intelligence.metadata.destination_countries array The list of countries targeted by scanning.
GreyNoise.IP.internet_scanner_intelligence.metadata.destination_country_codes array The list of country codes targeted by scanning.
GreyNoise.IP.internet_scanner_intelligence.metadata.domain string The domain associated with the IP address.
GreyNoise.IP.internet_scanner_intelligence.metadata.latitude number The latitude coordinate of the IP address location.
GreyNoise.IP.internet_scanner_intelligence.metadata.longitude number The longitude coordinate of the IP address location.
GreyNoise.IP.internet_scanner_intelligence.metadata.mobile boolean Whether the device is on a mobile network.
GreyNoise.IP.internet_scanner_intelligence.metadata.organization string The organization that owns the network that the IP address belongs to.
GreyNoise.IP.internet_scanner_intelligence.metadata.os string The name of the operating system of the device.
GreyNoise.IP.internet_scanner_intelligence.metadata.rdns string Reverse DNS lookup of the IP address.
GreyNoise.IP.internet_scanner_intelligence.metadata.rdns_parent string The parent domain of the reverse DNS lookup.
GreyNoise.IP.internet_scanner_intelligence.metadata.rdns_validated boolean Whether the reverse DNS lookup has been validated.
GreyNoise.IP.internet_scanner_intelligence.metadata.region string The full name of the region the device is geographically located in.
GreyNoise.IP.internet_scanner_intelligence.metadata.sensor_count number The number of sensors that observed activity from this IP.
GreyNoise.IP.internet_scanner_intelligence.metadata.sensor_hits number The number of sensor events recorded from this IP.
GreyNoise.IP.internet_scanner_intelligence.metadata.single_destination boolean Whether the IP targets a single destination.
GreyNoise.IP.internet_scanner_intelligence.metadata.source_city string The city where the IP is geographically located.
GreyNoise.IP.internet_scanner_intelligence.metadata.source_country string The full name of the IP source country.
GreyNoise.IP.internet_scanner_intelligence.metadata.source_country_code string The country code of the IP source country.
GreyNoise.IP.internet_scanner_intelligence.tor boolean Whether the device is a known Tor exit node.
GreyNoise.IP.internet_scanner_intelligence.raw_data.hassh.fingerprint string HASSH hash fingerprint string.
GreyNoise.IP.internet_scanner_intelligence.raw_data.hassh.port number TCP port connection where the HASSH hash was identified.
GreyNoise.IP.internet_scanner_intelligence.raw_data.http.md5 array MD5 hashes of HTTP requests made by the device.
GreyNoise.IP.internet_scanner_intelligence.raw_data.http.method array HTTP methods used by the device.
GreyNoise.IP.internet_scanner_intelligence.raw_data.http.path array HTTP paths the device has been observed accessing.
GreyNoise.IP.internet_scanner_intelligence.raw_data.http.request_header array HTTP request headers used by the device.
GreyNoise.IP.internet_scanner_intelligence.raw_data.http.useragent array HTTP user-agents the device has been observed using.
GreyNoise.IP.internet_scanner_intelligence.raw_data.ja3.fingerprint string The JA3 TLS/SSL fingerprint.
GreyNoise.IP.internet_scanner_intelligence.raw_data.ja3.port number The corresponding TCP port for the given JA3 fingerprint.
GreyNoise.IP.internet_scanner_intelligence.raw_data.scan.port number The port number(s) the device has been observed scanning.
GreyNoise.IP.internet_scanner_intelligence.raw_data.scan.protocol string The protocol of the port the device has been observed scanning.
GreyNoise.IP.internet_scanner_intelligence.raw_data.source.bytes number The number of bytes sent by the source.
GreyNoise.IP.internet_scanner_intelligence.raw_data.tls.cipher array TLS cipher suites used by the device.
GreyNoise.IP.internet_scanner_intelligence.raw_data.tls.ja4 array JA4 TLS/SSL fingerprints.
GreyNoise.IP.internet_scanner_intelligence.raw_data.web.paths array Any HTTP paths the device has been observed crawling the Internet for.
GreyNoise.IP.internet_scanner_intelligence.raw_data.web.useragents array Any HTTP user-agents the device has been observed using while crawling the Internet.
GreyNoise.IP.internet_scanner_intelligence.seen boolean Whether the IP is in record with GreyNoise.
GreyNoise.IP.internet_scanner_intelligence.spoofable boolean Whether the ip is spoofable.
GreyNoise.IP.internet_scanner_intelligence.tags.category string The category of the given tag.
GreyNoise.IP.internet_scanner_intelligence.tags.created date The date the tag was added to the GreyNoise system.
GreyNoise.IP.internet_scanner_intelligence.tags.description string A description of what the tag identifies.
GreyNoise.IP.internet_scanner_intelligence.tags.id string The unique id of the tag.
GreyNoise.IP.internet_scanner_intelligence.tags.intention string The intention of the associated activity the tag identifies.
GreyNoise.IP.internet_scanner_intelligence.tags.name string The name of the tag.
GreyNoise.IP.internet_scanner_intelligence.tags.recommend_block boolean Indicates if IPs associated with this tag should be blocked.
GreyNoise.IP.internet_scanner_intelligence.tags.references string A list of references used to create the tag.
GreyNoise.IP.internet_scanner_intelligence.tags.slug string The unique slug of the tag.
GreyNoise.IP.internet_scanner_intelligence.tags.updated_at date The date the tag was last updated.
GreyNoise.IP.internet_scanner_intelligence.vpn boolean Whether the device is a VPN endpoint or not.
GreyNoise.IP.internet_scanner_intelligence.vpn_service string The name of the VPN service provider of the device.
GreyNoise.Query.complete boolean Whether all results have been fetched or not.
GreyNoise.Query.count number Count of the total matching records.
GreyNoise.Query.message string Message from the API response.
GreyNoise.Query.query string Query which was used to filter the records.
GreyNoise.Query.scroll string Scroll token to paginate through results.

Command Example

!greynoise-query advanced_query=ip:64.39.108.148 spoofable=true

Human Readable Output

GreyNoise Internet Scanner Intelligence

Total findings: 1

Query: (ip:64.39.108.148 spoofable:true) last_seen:90d

GreyNoise Internet Scanner Intelligence

IP Internet Scanner Classification Actor Tags Spoofable VPN BOT Tor First Seen Last Seen Timestamp
64.39.108.148 true benign Qualys Qualys (benign - actor) true false false false 2025-05-25 2025-05-25 09:28:51

To view the detailed query result please click here.

greynoise-stats


Get aggregate statistics for the top organizations, actors, tags, ASNs, countries, classifications, and operating systems of all the results of a given GNQL query.

Base Command

greynoise-stats

Input

Argument Name Description Required
classification Classification of the device like unknown, benign, malicious. Possible values are: unknown, benign, malicious. Optional
spoofable Whether the IP is spoofable or not. Possible values are: true, false. Optional
actor The benign actor the device has been associated with. Optional
size Maximum amount of results to grab. Default is 10. Optional
advanced_query GNQL query to filter records. Note: It merges other arguments and takes higher precedence over the same argument if supplied. Example: malicious, spoofable:false SSH Scanner, spoofable:false classification:benign tags:POP3 Scanner cve:CVE-2010-0103. Optional
last_seen The date the device was most recently observed by GreyNoise. Example: 1d, 2d, 12h, or 1m. Optional
organization The organization that owns the network that the IP address belongs to. Optional

Context Output

Path Type Description
GreyNoise.Stats.adjusted_query string Provides the adjusted query, if the submitted one could not be executed as-is.
GreyNoise.Stats.query string The query which was used to filter the records.
GreyNoise.Stats.count number Count of total aggregated records.
GreyNoise.Stats.stats.classifications.classification string Classification name.
GreyNoise.Stats.stats.classifications.count number Classification count.
GreyNoise.Stats.stats.spoofable.spoofable boolean Whether records are spoofable or not.
GreyNoise.Stats.stats.spoofable.count number Spoofable count.
GreyNoise.Stats.stats.organizations.organization string Organization name.
GreyNoise.Stats.stats.organizations.count number Organization count.
GreyNoise.Stats.stats.actors.actor string Actor name.
GreyNoise.Stats.stats.actors.count number Actor count.
GreyNoise.Stats.stats.countries.country string Country name.
GreyNoise.Stats.stats.countries.count number Country count.
GreyNoise.Stats.stats.source_countries.country string Country name.
GreyNoise.Stats.stats.source_countries.count number Country count.
GreyNoise.Stats.stats.destination_countries.country string Country name.
GreyNoise.Stats.stats.destination_countries.count number Country count.
GreyNoise.Stats.stats.tags.tag string Tag name.
GreyNoise.Stats.stats.tags.id string Tag ID.
GreyNoise.Stats.stats.tags.count number Tag count.
GreyNoise.Stats.stats.operating_systems.operating_system string Operating system name.
GreyNoise.Stats.stats.operating_systems.count number Operating system count.
GreyNoise.Stats.stats.categories.category string Category name.
GreyNoise.Stats.stats.categories.count number Category count.
GreyNoise.Stats.stats.asns.asn string Asn name.
GreyNoise.Stats.stats.asns.count number Asn count.

Command Example

!greynoise-stats spoofable=true size=2 advanced_query="spoofable:false

Human Readable Output

GreyNoise Internet Scanner Intelligence

Stats Query

Total IP Count: 489889

Classifications

Classification Count
unknown 248634
malicious 127595
suspicious 103741
benign 9919

Spoofable

Spoofable Count
False 489889

Organizations

Organization Count
Mobile Communication Company of Iran PLC 58005
National Internet Backbone 30561
CHINA UNICOM China169 Backbone 26144
CHINANET-BACKBONE 19036
Iran Telecommunication Company PJS 17789
Iran Cell Service and Communication Company 17343
Cloudflare, Inc. 13137
DigitalOcean, LLC 8490
Telecom International Myanmar Co., Ltd 5160

Actors

Actor Count
Stretchoid 2008
Cortex Xpanse 1983
GoogleBot 1142
Alpha Strike Labs 1018
ShadowServer.org 983
Bytespider 896
BinaryEdge.io 756
Driftnet 609
ONYPHE 576

Source Countries

Country Count
Iran 106580
China 58763
India 48230
United States 32369
Russia 14796
Myanmar 12047
Germany 11446
Singapore 7643
Brazil 6892

Destination Countries

Country Count
United States 407724
India 271246
Singapore 241697
United Kingdom 162690
Germany 133679
Japan 124237
Spain 121176
Canada 108688
Mexico 104668
France 103558

Tags

Tag Count
Web Crawler 177741
TLS/SSL Crawler 157135
Telnet Login Attempt 63236
SSH Connection Attempt 62197
SMBv1 Crawler 62062
Telnet Bruteforcer 58469
Go HTTP Client 53544
Generic IoT Default Password Attempt 40313
Mirai 33110
Mirai TCP Scanner 30260

Categories

Category Count
isp 307235
hosting 86812
business 6373
education 952
government 421

ASNs

ASN Count
AS197207 58005
AS9829 30561
AS4837 26144
AS4134 19036
AS58224 17789
AS44244 17343
AS13335 13100
AS14061 8490
AS136255 5160

greynoise-riot


Identifies IPs from known benign services and organizations that commonly cause false positives in network security and threat intelligence products. The collection of IPs in RIOT is continually curated and verified to provide accurate results. These IPs are extremely unlikely to pose a threat to your network.

Base Command

greynoise-riot

Input

Argument Name Description Required
ip The IP address to be checked if it is potentially harmful or not. Required

Context Output

Path Type Description
DBotScore.Indicator String The indicator that was tested.
DBotScore.Reliability String The reliability of the data.
DBotScore.Score Number The actual score.
DBotScore.Type String The indicator type.
DBotScore.Vendor String The vendor used to calculate the score.
GreyNoise.IP.category String The category of the IP if riot is “True”.
GreyNoise.IP.classification String The classification of the IP if riot is “True”.
GreyNoise.IP.description String The description of the IP if riot is “True”.
GreyNoise.IP.explanation String The explanation of the IP if riot is “True”.
GreyNoise.IP.found String Indicates if the IP is business service.
GreyNoise.IP.last_updated Date When was the last time the business service information was updated.
GreyNoise.IP.ip String The IP to query.
GreyNoise.IP.name String The name of the IP if the riot is “True”.
GreyNoise.IP.reference String The reference of the IP if riot is “True”.
GreyNoise.IP.riot String Indicates if the IP is business service.
GreyNoise.IP.trust_level String The trust level of the IP if riot is “True”.

Example Command

!greynoise-riot ip="64.39.108.148"

Human Readable Output

IP: 64.39.108.148 found with Reputation: Good

Belongs to Common Business Service: Qualys

GreyNoise Business Service Intelligence Lookup

IP Business Service Category Name Trust Level Description Last Updated
64.39.108.148 true vulnerability_management Qualys 1 - Reasonably Ignore Qualys Inc (Qualys) is a provider of cloud-based platform information security and compliance cloud solutions. The company’s cloud platform offers private cloud platforms, private cloud platform appliances, public cloud integrations, and cloud agents. 2025-06-26T13:10:55Z

greynoise-context


Identifies IPs that have been observed mass-scanning the internet.

Base Command

greynoise-context

Input

Argument Name Description Required
ip The IP address to query in GreyNoise Context Command. Required

Context Output

Path Type Description
DBotScore.Indicator String The indicator that was tested.
DBotScore.Reliability String The reliability of the data.
DBotScore.Score Number The actual score.
DBotScore.Type String The indicator type.
DBotScore.Vendor String The vendor used to calculate the score.
GreyNoise.IP.address string The IP address of the scanning device IP.
GreyNoise.IP.actor string The overt actor the device has been associated with.
GreyNoise.IP.bot Boolean Whether the IP is associated with known bot activity or not. Common examples include credential stuffing, content scraping, or brute force attacks.
GreyNoise.IP.classification string Whether the device has been categorized as unknown, benign, or malicious.
GreyNoise.IP.cve array CVEs associated with IP.
GreyNoise.IP.first_seen date The date the device was first observed by GreyNoise. Format is ISO8601.
GreyNoise.IP.found boolean Whether the IP was found in GreyNoise records.
GreyNoise.IP.last_seen date The date the device was last observed by GreyNoise. Format is ISO8601.
GreyNoise.IP.last_seen_timestamp string The timestamp when the device was last observed by GreyNoise.
GreyNoise.IP.metadata.asn string The autonomous system identification number.
GreyNoise.IP.metadata.carrier string The carrier information for the IP address.
GreyNoise.IP.metadata.category string Whether the device belongs to a business, isp, hosting, education, or mobile network.
GreyNoise.IP.metadata.city string The city the device is geographically located in.
GreyNoise.IP.metadata.country string The full name of the country.
GreyNoise.IP.metadata.country_code string The two-character country code of the country.
GreyNoise.IP.metadata.datacenter string The datacenter information for the IP address.
GreyNoise.IP.metadata.destination_asns array The list of ASNs targeted by scanning.
GreyNoise.IP.metadata.destination_cities array The list of cities targeted by scanning.
GreyNoise.IP.metadata.destination_countries array The list of countries targeted by scanning.
GreyNoise.IP.metadata.destination_country_codes array The list of country codes targeted by scanning.
GreyNoise.IP.metadata.domain string The domain associated with the IP address.
GreyNoise.IP.metadata.latitude number The latitude coordinate of the IP address location.
GreyNoise.IP.metadata.longitude number The longitude coordinate of the IP address location.
GreyNoise.IP.metadata.mobile boolean Whether the device is on a mobile network.
GreyNoise.IP.metadata.organization string The organization that owns the network that the IP address belongs to.
GreyNoise.IP.metadata.os string The name of the operating system of the device.
GreyNoise.IP.metadata.rdns string Reverse DNS lookup of the IP address.
GreyNoise.IP.metadata.rdns_parent string The parent domain of the reverse DNS lookup.
GreyNoise.IP.metadata.rdns_validated boolean Whether the reverse DNS lookup has been validated.
GreyNoise.IP.metadata.region string The full name of the region the device is geographically located in.
GreyNoise.IP.metadata.sensor_count number The number of sensors that observed activity from this IP.
GreyNoise.IP.metadata.sensor_hits number The number of sensor events recorded from this IP.
GreyNoise.IP.metadata.single_destination boolean Whether the IP targets a single destination.
GreyNoise.IP.metadata.source_city string The city where the IP is geographically located.
GreyNoise.IP.metadata.source_country string The full name of the IP source country.
GreyNoise.IP.metadata.source_country_code string The country code of the IP source country.
GreyNoise.IP.metadata.tor boolean Whether the device is a known Tor exit node.
GreyNoise.IP.tor boolean Whether the device is a known Tor exit node.
GreyNoise.IP.raw_data.hassh.fingerprint string HASSH hash fingerprint string.
GreyNoise.IP.raw_data.hassh.port number TCP port connection where the HASSH hash was identified.
GreyNoise.IP.raw_data.http.md5 array MD5 hashes of HTTP requests made by the device.
GreyNoise.IP.raw_data.http.method array HTTP methods used by the device.
GreyNoise.IP.raw_data.http.path array HTTP paths the device has been observed accessing.
GreyNoise.IP.raw_data.http.request_header array HTTP request headers used by the device.
GreyNoise.IP.raw_data.http.useragent array HTTP user-agents the device has been observed using.
GreyNoise.IP.raw_data.ja3.fingerprint string The JA3 TLS/SSL fingerprint.
GreyNoise.IP.raw_data.ja3.port number The corresponding TCP port for the given JA3 fingerprint.
GreyNoise.IP.raw_data.tls.ja4 array JA4 TLS/SSL fingerprints.
GreyNoise.IP.raw_data.scan.port number The port number(s) the device has been observed scanning.
GreyNoise.IP.raw_data.scan.protocol string The protocol of the port the device has been observed scanning.
GreyNoise.IP.raw_data.source.bytes number The number of bytes sent by the source.
GreyNoise.IP.raw_data.tls.cipher array TLS cipher suites used by the device.
GreyNoise.IP.raw_data.tls.ja4 array JA4 TLS/SSL fingerprints.
GreyNoise.IP.raw_data.web.paths array Any HTTP paths the device has been observed crawling the Internet for.
GreyNoise.IP.raw_data.web.useragents array Any HTTP user-agents the device has been observed using while crawling the Internet.
GreyNoise.IP.seen boolean Whether the IP is in record with GreyNoise.
GreyNoise.IP.spoofable boolean Whether the ip is spoofable.
GreyNoise.IP.tags.category string The category of the given tag.
GreyNoise.IP.tags.created date The date the tag was added to the GreyNoise system.
GreyNoise.IP.tags.description string A description of what the tag identifies.
GreyNoise.IP.tags.id string The unique id of the tag.
GreyNoise.IP.tags.intention string The intention of the associated activity the tag identifies.
GreyNoise.IP.tags.name string The name of the tag.
GreyNoise.IP.tags.recommend_block boolean Indicates if IPs associated with this tag should be blocked.
GreyNoise.IP.tags.references string A list of references used to create the tag.
GreyNoise.IP.tags.slug string The unique slug of the tag.
GreyNoise.IP.tags.updated_at date The date the tag was last updated.
GreyNoise.IP.vpn boolean Whether the device is a VPN endpoint or not.
GreyNoise.IP.vpn_service string The name of the VPN service provider of the device.

Example Command

!greynoise-context ip="114.119.130.178"

Human Readable Output

IP: 64.39.108.148 found with Reputation: Good

GreyNoise Internet Scanner Intelligence Lookup

IP Internet Scanner Classification Actor Tags Spoofable VPN BOT Tor First Seen Last Seen Timestamp
64.39.108.148 true benign Qualys Qualys (benign - actor) true false false false 2025-05-25 2025-05-25 09:28:51

greynoise-similarity


Identify IPs with a similar internet scanning profile.

Base Command

greynoise-similarity

Input

Argument Name Description Required
ip The IP address to find similar IPs for Required
minimum_score The similar score to return results above. Valid from 85 to 100. Default is 90. Optional
maximum_results The maximum number of similar results to return. Default is 50. Optional

Context Output

Path Type Description
GreyNoise.Similar.ip string The IP address of the scanning device IP.
GreyNoise.Similar.first_seen date The date the device was first observed by GreyNoise. Format is ISO8601.
GreyNoise.Similar.last_seen date The date the device was last observed by GreyNoise. Format is ISO8601.
GreyNoise.Similar.actor string The overt actor the device has been associated with.
GreyNoise.Similar.classification string Whether the device has been categorized as unknown, benign, or malicious.
GreyNoise.Similar.asn string The autonomous system identification number.
GreyNoise.Similar.city string The city the device is geographically located in.
GreyNoise.Similar.country string The full name of the country.
GreyNoise.Similar.country_code string The two-character country code of the country.
GreyNoise.Similar.organization string The organization that owns the network that the IP address belongs to.
GreyNoise.Similar.similar_ips array Details of similar IPs

Command Example

!greynoise-similarity ip="1.2.3.4" minimum_score="90" maximum_results="50"

Human Readable Output - Results

IP: 59.88.225.2 - Similar Internet Scanners found in GreyNoise
Total Similar IPs with Score above 90%: 100
Displaying 50 results below. To see all results, visit the GreyNoise Visualizer.
GreyNoise Similar IPs

IP Score Classification Actor Organization Last Seen Similarity Features
1.2.3.4 100 malicious unknown GoogleBot 2023-04-05 ports,spoofable_bool

!greynoise-similarity ip="114.119.130.178"

Human Readable Output - No Results

GreyNoise Similarity Lookup returned No Results.

greynoise-timeline


Get timeline activity for an IP address.

Base Command

greynoise-timeline

Input

Argument Name Description Required
ip The IP address to get timeline activity for Required
days The number of days from today to get activity. Valid from 1 to 90. Default is 30. Optional
maximum_results The maximum number of similar results to return. Default is 50. Optional

Context Output

Path Type Description
GreyNoise.Timeline.ip string The IP address of the scanning device IP.
GreyNoise.Timeline.metadata.start_time date The start time of the activity period
GreyNoise.Timeline.metadata.end_time date The end time of the activity period
GreyNoise.Timeline.metadata.limit string Limit of activity events returned
GreyNoise.Timeline.metadata.next_cursor string Cursor value to pull next page of results
GreyNoise.Timeline.activity array Daily activity summaries

Command Example

!greynoise-timeline ip="1.1.2.2" days="30" maximum_results="30"

Human Readable Output - Results

IP: 45.164.214.212 - GreyNoise IP Timeline
Internet Scanner Timeline Details - Daily Activity Summary

Date Classification Tags rDNS Organization ASN Ports Web Paths User Agents
1.2.3.4 malicious BruteForcer me.acme.lcl Acme, Inc AS12345 ports,spoofable_bool /root/home MozillaFirefox

Human Readable Output - No Results

GreyNoise IP Timeline Returned No Results.

cve


Queries GreyNoise for CVE Vuln Intelligence.

Base Command

cve

Input

Argument Name Description Required
cve A comma-separated list of CVE IDs. Required

Context Output

Path Type Description
CVE.ID string CVE ID.
GreyNoise.CVE.details.vulnerability_name String The vulnerability name.
GreyNoise.CVE.details.vulnerability_description String A description of the vulnerability.
GreyNoise.CVE.details.cve_cvss_score Number The CVSS score.
GreyNoise.CVE.details.product String The vulnerable product.
GreyNoise.CVE.details.vendor String The vendor that produces the vulnerable product.
GreyNoise.CVE.details.published_to_nist_nvd Boolean Is this CVE published to NIST NVD?
GreyNoise.CVE.timeline.cve_published_date Date When was the CVE published.
GreyNoise.CVE.timeline.cve_last_updated_date Date When was the CVE information last updated.
GreyNoise.CVE.timeline.first_known_published_date Date When first exploit associated with CVE was published.
GreyNoise.CVE.timeline.cisa_kev_date_added Date When the CVE was added to KEV.
GreyNoise.CVE.exploitation_details.attack_vector String The attack vector category.
GreyNoise.CVE.exploitation_details.exploit_found Boolean Whether any known exploits are available.
GreyNoise.CVE.exploitation_details.exploitation_registered_in_kev Boolean Whether exploitation has been registered in KEV database.
GreyNoise.CVE.exploitation_details.epss_score Number EPSS score associated with this exploitation (Exploit Prediction Scoring System).
GreyNoise.CVE.exploitation_stats.number_of_available_exploits Number The total number of exploits available (public + commercial).
GreyNoise.CVE.exploitation_stats.number_of_threat_actors_exploiting_vulnerability Number The total number of known threat actors.
GreyNoise.CVE.exploitation_stats.number_of_botnets_exploiting_vulnerability Number The total number of botnets.
GreyNoise.CVE.exploitation_activity.activity_seen Boolean Whether GreyNoise has seen activity.
GreyNoise.CVE.exploitation_activity.benign_ip_count_1d Number The total number of benign IP addresses GreyNoise has seen exercising (Scanning or Exploiting) this vulnerability in the last day.
GreyNoise.CVE.exploitation_activity.benign_ip_count_10d Number The total number of benign IP addresses GreyNoise has seen exercising (Scanning or Exploiting) this vulnerability in the last 10 days.
GreyNoise.CVE.exploitation_activity.benign_ip_count_30d Number The total number of benign IP addresses GreyNoise has seen exercising (Scanning or Exploiting) this vulnerability in the last 30 days.
GreyNoise.CVE.exploitation_activity.threat_ip_count_1d Number The total number of threat IP addresses GreyNoise has seen exercising (Scanning or Exploiting) this vulnerability in the last day.
GreyNoise.CVE.exploitation_activity.threat_ip_count_10d Number The total number of threat IP addresses GreyNoise has seen exercising (Scanning or Exploiting) this vulnerability in the last 10 days.
GreyNoise.CVE.exploitation_activity.threat_ip_count_30d Number The total number of threat IP addresses GreyNoise has seen exercising (Scanning or Exploiting) this vulnerability in the last 30 days.
DBotScore.Indicator String The indicator that was tested.
DBotScore.Type String The indicator type.
DBotScore.Vendor String The vendor used to calculate the score.
DBotScore.Score Number The actual score.

Example Command

!cve cve="CVE-2021-26086"

Human Readable Output

CVE: CVE-2021-26086 is found

GreyNoise CVE Lookup

CVE ID CVSS Vendor Product Published to NVD
CVE-2021-26086 5.3 Atlassian Jira Server and Data Center true

Timeline Details

Added to Kev Last Updated CVE Published First Published
2024-11-12 2025-02-09 2021-08-16 2023-11-18

Exploitation Details

Attack Vector EPSS Base Score Exploit Found Exploit Registered in KEV
NETWORK 0.94247 true true

Exploitation Stats

# of Available Exploits # of Botnets Exploiting # of Threat Actors Exploiting
4 1 1

Exploitation Activity - GreyNoise Insights

GreyNoise Observed Activity # of Benign IPs - Last Day # of Benign IPs - Last 10 Days # of Benign IPs - Last 30 Days # of Threat IPs - Last Day # of Threat IPs - Last 10 Days # of Threat IPs - Last 30 Days
true 14 15 15 126 164 261

Configuration parameters

  • integrationReliability — Integration Reliability
  • feedExpirationPolicy
  • feedExpirationInterval
  • apikey — API Key
  • credentials — API Key
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (9)

  • cve

    Queries GreyNoise for CVE Vuln Intelligence.

  • greynoise-context

    Identifies IPs that have been observed mass-scanning the internet.

  • greynoise-ip-quick-check

    Check whether a given IP address is "Internet background noise", or has been observed scanning or attacking devices across the Internet. Note: It checks against the last 60 days of Internet scanner data.

  • greynoise-query

    Get the information of IP based on the providence filters.

  • greynoise-riot

    Identifies IPs from known benign services and organizations that commonly cause false positives in network security and threat intelligence products. The collection of IPs in RIOT is continually curated and verified to provide accurate results. These IPs are extremely unlikely to pose a threat to your network.

  • greynoise-similarity

    Search for similar internet scanner IPs found in the GreyNoise Noise (internet scanner) dataset.

  • greynoise-stats

    Get aggregate statistics for the top organizations, actors, tags, ASNs, countries, classifications, and operating systems of all the results of a given GNQL query.

  • greynoise-timeline

    Retrieve daily scanner timeline information for an Internet Scanner.

  • ip

    Runs reputation on IPs.

from CommonServerPython import *

""" Imports """
import copy
import re
import traceback
from typing import Any

import requests
import urllib3  # type: ignore
from greynoise.api import GreyNoise, APIConfig  # type: ignore
from greynoise import exceptions, util  # type: ignore

# Disable insecure warnings
urllib3.disable_warnings()
util.LOGGER.warning = util.LOGGER.debug

""" CONSTANTS """

TIMEOUT = 30
PRETTY_KEY = {
    "ip": "IP",
    "first_seen": "First Seen",
    "last_seen": "Last Seen",
    "last_seen_timestamp": "Last Seen Timestamp",
    "seen": "Internet Scanner",
    "tags": "Tags",
    "actor": "Actor",
    "spoofable": "Spoofable",
    "classification": "Classification",
    "cve": "CVE",
    "metadata": "MetaData",
    "asn": "ASN",
    "city": "City",
    "country": "Country",
    "country_code": "Country Code",
    "destination_countries": "Destination Countries",
    "destination_country_codes": "Destination Country Codes",
    "organization": "Organization",
    "category": "Category",
    "sensor_count": "Sensor Count",
    "sensor_hits": "Sensor Hits",
    "source_country": "Source Country",
    "source_country_code": "Source Country Code",
    "tor": "Tor",
    "rdns": "rDNS",
    "os": "OS",
    "region": "Region",
    "vpn": "VPN",
    "vpn_service": "VPN Service",
    "raw_data": "Raw Data",
    "scan": "Scan",
    "port": "Port",
    "protocol": "Protocol",
    "web": "Web",
    "path": "Web Paths",
    "useragent": "User-Agents",
    "ja3": "JA3",
    "fingerprint": "Fingerprint",
    "hassh": "HASSH",
    "bot": "BOT",
    "ja4": "JA4",
    "cipher": "Cipher",
    "md5": "MD5",
}
IP_CONTEXT_HEADERS = [
    "IP",
    "Internet Scanner",
    "Classification",
    "Actor",
    "CVE",
    "Tags",
    "Spoofable",
    "VPN",
    "BOT",
    "Tor",
    "First Seen",
    "Last Seen Timestamp",
]
SIMILAR_HEADERS = ["IP", "Score", "Classification", "Actor", "Organization", "Source Country", "Last Seen", "Similarity Features"]
TIMELINE_HEADERS = [
    "Date",
    "Classification",
    "Tags",
    "rDNS",
    "Organization",
    "ASN",
    "Ports",
    "Web Paths",
    "User Agents",
]
RIOT_HEADERS = ["IP", "Business Service", "Category", "Name", "Trust Level", "Description", "Last Updated"]
API_SERVER = util.DEFAULT_CONFIG.get("api_server")
IP_QUICK_CHECK_HEADERS = ["IP", "Internet Scanner", "Classification", "Business Service", "Trust Level"]
STATS_KEY = {
    "classifications": "Classifications",
    "spoofable": "Spoofable",
    "organizations": "Organizations",
    "actors": "Actors",
    "source_countries": "Source Countries",
    "destination_countries": "Destination Countries",
    "tags": "Tags",
    "operating_systems": "Operating Systems",
    "categories": "Categories",
    "asns": "ASNs",
}
STATS_H_KEY = {
    "classification": "Classification",
    "spoofable": "Spoofable",
    "organization": "Organization",
    "actor": "Actor",
    "country": "Country",
    "tag": "Tag",
    "operating_system": "Operating System",
    "category": "Category",
    "asn": "ASN",
    "count": "Count",
}
QUERY_OUTPUT_PREFIX: dict[str, str] = {
    "IP": "GreyNoise.IP(val.address && val.address == obj.address)",
    "QUERY": "GreyNoise.Query(val.query && val.query == obj.query)",
}
EXCEPTION_MESSAGES = {
    "API_RATE_LIMIT": "API Rate limit hit. Try after sometime.",
    "UNAUTHENTICATED": "Unauthenticated. Check the configured API Key.",
    "COMMAND_FAIL": "Failed to execute {} command.\n Error: {}",
    "SERVER_ERROR": "The server encountered an internal error for GreyNoise and was unable to complete your request.",
    "CONNECTION_TIMEOUT": "Connection timed out. Check your network connectivity.",
    "PROXY": "Proxy Error - cannot connect to proxy. Either try clearing the 'Use system proxy' check-box or check "
    "the host, authentication details and connection details for the proxy.",
    "INVALID_RESPONSE": "Invalid response from GreyNoise. Response: {}",
    "QUERY_STATS_RESPONSE": "GreyNoise request failed. Reason: {}",
}

""" CLIENT CLASS """


class Client(GreyNoise):
    """Client class to interact with the service API"""

    def authenticate(self):
        """
        Used to authenticate GreyNoise credentials.
        """
        try:
            self.test_connection()

            return "ok"

        except exceptions.RateLimitError:
            raise DemistoException(EXCEPTION_MESSAGES["API_RATE_LIMIT"])

        except exceptions.RequestFailure as err:
            status_code = err.args[0]
            body = str(err.args[1])

            if status_code == 401:
                raise DemistoException(EXCEPTION_MESSAGES["UNAUTHENTICATED"])
            elif status_code == 429:
                raise DemistoException(EXCEPTION_MESSAGES["API_RATE_LIMIT"])
            elif 400 <= status_code < 500:
                raise DemistoException(EXCEPTION_MESSAGES["COMMAND_FAIL"].format(demisto.command(), body))
            elif status_code >= 500:
                raise DemistoException(EXCEPTION_MESSAGES["SERVER_ERROR"])
            else:
                raise DemistoException(str(err))
        except requests.exceptions.ConnectTimeout:
            raise DemistoException(EXCEPTION_MESSAGES["CONNECTION_TIMEOUT"])
        except requests.exceptions.ProxyError:
            raise DemistoException(EXCEPTION_MESSAGES["PROXY"])


""" HELPER FUNCTIONS """


def exception_handler(func: Any) -> Any:
    """
    Decorator to handle all type of errors possible with GreyNoise SDK.
    """

    def inner_func(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except exceptions.RateLimitError:
            raise DemistoException(EXCEPTION_MESSAGES["API_RATE_LIMIT"])
        except exceptions.RequestFailure as err:
            status_code, body = parse_code_and_body(str(err))
            if status_code == 401 and "forbidden" in body:
                raise DemistoException(EXCEPTION_MESSAGES["UNAUTHENTICATED"])
            elif 400 <= status_code < 500:
                raise DemistoException(EXCEPTION_MESSAGES["COMMAND_FAIL"].format(demisto.command(), body))
            elif status_code >= 500:
                raise DemistoException(EXCEPTION_MESSAGES["SERVER_ERROR"])
            else:
                raise DemistoException(str(err))
        except requests.exceptions.ConnectTimeout:
            raise DemistoException(EXCEPTION_MESSAGES["CONNECTION_TIMEOUT"])
        except requests.exceptions.ProxyError:
            raise DemistoException(EXCEPTION_MESSAGES["PROXY"])

    return inner_func


def parse_code_and_body(message: str) -> tuple[int, str]:
    """Parse status code and body

    Parses code and body from the Exception raised by GreyNoise SDK.

    :type message: ``str``
    :param message: Exception message.

    :return: response code and response body.
    :rtype: ``tuple``
    """
    re_response = re.search(r"\(([0-9]+), (.*)\)", message)  # NOSONAR
    if re_response:
        code, body = re_response.groups()
        body = body.strip("'")
    else:
        return 0, message
    return int(code), body


def get_ip_context_data(responses: list) -> list:
    """Parse ip context and raw data from GreyNoise SDK response.

    Returns value of ip context data.
    Returns value of ip raw data.

    :type responses: ``list``
    :param responses: list of values of ip-context or ip-query.

    :return: list of ips context data.
    :rtype: ``list``
    """

    ip_context_responses = []

    responses = remove_empty_elements(responses)
    for response in responses:
        metadata_list: list = []
        tmp_response: dict = {}
        tags = get_ip_tag_names(response.get("tags", []))
        response["tags"] = tags
        for key, value in response.get("metadata", {}).items():
            if value != "":
                metadata_list.append(f"{PRETTY_KEY.get(key, key)}: {value}")
        tmp_response["MetaData"] = metadata_list

        for key, value in response.items():
            if value != "" and key not in ["metadata", "raw_data"]:
                tmp_response[PRETTY_KEY.get(key, key)] = value

        ip = tmp_response["IP"]
        tmp_response["IP"] = f"[{ip}](https://viz.greynoise.io/ip/{ip})"

        ip_context_responses.append(tmp_response)

    return ip_context_responses


def get_ip_reputation_score(classification: str) -> tuple[int, str]:
    """Get DBot score and human-readable of score.

    :type classification: ``str``
    :param classification: classification of ip provided from GreyNoise.

    :return: tuple of dbot score and it's readable form.
    :rtype: ``tuple``
    """
    if classification == "benign":
        return Common.DBotScore.GOOD, "Good"
    elif classification == "suspicious":
        return Common.DBotScore.SUSPICIOUS, "Suspicious"
    elif classification == "malicious":
        return Common.DBotScore.BAD, "Bad"
    else:
        return Common.DBotScore.NONE, "Unknown"


def get_ip_tag_names(tags: list) -> list:
    """Get tag names from tags list.

    :type tags: ``list``
    :param tags: list of tags.

    :return: list of tag names.
    :rtype: ``list``
    """
    tag_names = []
    for tag in tags:
        tag_name = tag.get("name") + " (" + tag.get("intention") + " - " + tag.get("category") + ")"
        tag_names.append(tag_name)

    return tag_names


def generate_advanced_query(args: dict) -> str:
    """Generate advance query for GreyNoise from args.

    :type args: ``dict``
    :param args: All command arguments, usually passed from ``demisto.args()``.

    :return: advanced query.
    :rtype: ``str``
    """

    advanced_query = args.get("advanced_query", "")
    used_args: dict = {
        "actor": args.get("actor"),
        "classification": args.get("classification"),
        "spoofable": args.get("spoofable"),
        "last_seen": args.get("last_seen"),
        "organization": args.get("organization"),
        "cve": args.get("cve"),
    }

    if advanced_query:
        advanced_query = advanced_query.replace(": ", ":")
        advanced_query = advanced_query.replace(" :", ":")

    arg_list = list(used_args.keys())
    arg_list.sort()

    for each in arg_list:
        if used_args[each] and f"{each}:" not in advanced_query:
            advanced_query += f" {each}:{used_args.get(each)}"

    advanced_query = advanced_query.strip(" ")

    if not advanced_query:
        advanced_query = "spoofable:false"

    return advanced_query


""" COMMAND FUNCTIONS """


@exception_handler
@logger
def test_module(client: Client) -> str:
    """Tests API connectivity and authentication'

    Returning 'ok' indicates that the integration works like it is supposed to.
    Connection to the service is successful.
    Raises exceptions if something goes wrong.

    :type client: ``Client``
    :param client: Client object for interaction with GreyNoise.

    :return: 'ok' if test passed, anything else will fail the test.
    :rtype: ``str``
    """
    return client.authenticate()


@exception_handler
@logger
def ip_quick_check_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """Check whether a given IP address is Internet Background Noise,
    or has been observed scanning or attacking devices across the internet.
        :type client: ``Client``
        :param client: Client object for interaction with GreyNoise.

        :type args: ``Dict``
        :param args: All command arguments, usually passed from ``demisto.args()``.

        :return: A ``CommandResults`` object that is then passed to ``return_results``,
            that contains the IP information.
        :rtype: ``CommandResults``
    """

    ip_address = argToList(args.get("ip"), ",")

    response = client.quick(ip_address)
    if not isinstance(response, list):
        raise DemistoException(EXCEPTION_MESSAGES["INVALID_RESPONSE"].format(response))

    original_response = copy.deepcopy(response)
    hr_list = []
    for record in response:
        hr_record = {
            "IP": record.get("ip") or record.get("address"),
            "Internet Scanner": record["internet_scanner_intelligence"].get("found"),
            "Classification": record["internet_scanner_intelligence"].get("classification"),
            "Business Service": record["business_service_intelligence"].get("found"),
            "Trust Level": record["business_service_intelligence"].get("trust_level"),
        }
        ip = hr_record["IP"]
        hr_record["IP"] = f"[{ip}](https://viz.greynoise.io/ip/{ip})"
        hr_list.append(hr_record)

    hr = tableToMarkdown(name="GreyNoise Quick IP Lookup Details", t=hr_list, headers=IP_QUICK_CHECK_HEADERS, removeNull=True)
    for resp in response:
        if "ip" in resp:
            resp["address"] = resp["ip"]
            del resp["ip"]

    return CommandResults(
        outputs_prefix="GreyNoise.IP",
        outputs_key_field="address",
        outputs=remove_empty_elements(response),
        readable_output=hr,
        raw_response=original_response,
    )


@exception_handler
@logger
def ip_reputation_command(client: Client, args: dict, reliability: str) -> List[CommandResults]:
    """Get information about a given IP address. Returns classification (benign, malicious or unknown),
        IP metadata (network owner, ASN, reverse DNS pointer, country), associated actors, activity tags,
        and raw port scan and web request information.

    :type client: ``Client``
    :param client: Client object for interaction with GreyNoise.

    :type args: ``dict``
    :param args: All command arguments, usually passed from ``demisto.args()``.

    :return: A list of ``CommandResults`` object that is then passed to ``return_results``,
        that contains the IP information.
    :rtype: ``List[CommandResults]``

    :type reliability: ``String``
    :param reliability: string
    """
    ips = argToList(args["ip"])
    command_results = []
    for ip in ips:
        try:
            api_response = client.ip(ip)
        except Exception as e:
            raise DemistoException(EXCEPTION_MESSAGES["INVALID_RESPONSE"].format(e))

        if not isinstance(api_response, dict) or (
            "internet_scanner_intelligence" not in api_response and "business_service_intelligence" not in api_response
        ):
            raise DemistoException(EXCEPTION_MESSAGES["INVALID_RESPONSE"].format(api_response))

        if "internet_scanner_intelligence" in api_response:
            response = api_response["internet_scanner_intelligence"]
            response["seen"] = response.get("found", False)
            response["address"] = api_response["ip"]
            response["ip"] = api_response["ip"]
            riot_response = api_response["business_service_intelligence"]
            riot_response["riot"] = riot_response.get("found", False)
            riot_response["address"] = api_response["ip"]
            tmp_response = get_ip_context_data([response])
        else:
            response = {}
            riot_response = {}
            tmp_response = []

        original_response = copy.deepcopy(api_response)

        response = remove_empty_elements(response)

        riot_response = remove_empty_elements(riot_response)

        if riot_response["riot"]:
            if riot_response["trust_level"] == "1":
                riot_response["classification"] = "benign"
                riot_response["trust_level"] = "1 - Reasonably Ignore"
            elif riot_response["trust_level"] == "2":
                riot_response["classification"] = "unknown"
                riot_response["trust_level"] = "2 - Commonly Seen"
            elif riot_response["trust_level"] == "3":
                riot_response["classification"] = "unknown"
                riot_response["trust_level"] = "3 - Context Only"
            if riot_response.get("logo_url", "") != "":
                del riot_response["logo_url"]

        if response["seen"] and response["classification"] == "malicious":
            malicious_description = "This IP has been observed scanning the internet in a malicious manner."
        else:
            malicious_description = ""

        if response["seen"] and not riot_response["riot"]:
            dbot_score_int, dbot_score_string = get_ip_reputation_score(response.get("classification"))

            human_readable = f"### IP: {ip} found with Reputation: {dbot_score_string}\n"
            human_readable += tableToMarkdown(
                name="GreyNoise Internet Scanner Intelligence Lookup", t=tmp_response, headers=IP_CONTEXT_HEADERS, removeNull=True
            )

            riot_tmp_response = {"IP": riot_response.get("address"), "Business Service": riot_response.get("riot")}

            human_readable += f"### IP: {ip} Not Associated with a Business Service\n"
            human_readable += tableToMarkdown(
                name="GreyNoise Business Service Intelligence Lookup",
                t=riot_tmp_response,
                headers=["IP", "Business Service"],
                removeNull=False,
            )

            dbot_score = Common.DBotScore(
                indicator=response.get("address"),
                indicator_type=DBotScoreType.IP,
                score=dbot_score_int,
                integration_name="GreyNoise",
                malicious_description=malicious_description,
                reliability=reliability,
            )

            city = response.get("metadata", {}).get("source_city", "")
            region = response.get("metadata", {}).get("region", "")
            country_code = response.get("metadata", {}).get("source_country_code", "")
            geo_description = (
                f"City: {city}, Region: {region}, Country Code: {country_code}" if (city or region or country_code) else ""
            )
            ip_standard_context = Common.IP(
                ip=response.get("address"),
                asn=response.get("metadata", {}).get("asn"),
                hostname=response.get("actor"),
                geo_country=response.get("metadata", {}).get("source_country"),
                geo_description=geo_description,
                dbot_score=dbot_score,
            )

            command_results.append(
                CommandResults(
                    readable_output=human_readable,
                    outputs_prefix="GreyNoise.IP",
                    outputs_key_field="address",
                    outputs=response,
                    indicator=ip_standard_context,
                    raw_response=original_response,
                )
            )

        if riot_response["riot"] and not response["seen"]:
            riot_tmp_response = {
                "IP": f"[{riot_response.get('address')}](https://viz.greynoise.io/ip/{riot_response.get('address')})",
                "Business Service": riot_response.get("riot"),
                "Name": riot_response.get("name"),
                "Category": riot_response.get("category"),
                "Trust Level": riot_response.get("trust_level"),
                "Description": riot_response.get("description"),
                "Last Updated": riot_response.get("last_updated"),
            }

            dbot_score_int, dbot_score_string = get_ip_reputation_score(riot_response.get("classification"))

            human_readable = f"### IP: {ip} found with Reputation: {dbot_score_string}\n"
            human_readable += f"#### Belongs to Common Business Service: {riot_response.get('name', 'Unknown')}\n"
            human_readable += tableToMarkdown(
                name="GreyNoise Business Service Intelligence Lookup", t=riot_tmp_response, headers=RIOT_HEADERS, removeNull=False
            )
            tmp_response = [{"IP": response.get("address"), "Internet Scanner": response.get("seen")}]

            human_readable += f"### IP: {ip} No Mass-Internet Scanning Observed\n"
            human_readable += tableToMarkdown(
                name="GreyNoise Internet Scanner Intelligence Lookup",
                t=tmp_response,
                headers=["IP", "Internet Scanner"],
                removeNull=False,
            )

            dbot_score = Common.DBotScore(
                indicator=response.get("address"),
                indicator_type=DBotScoreType.IP,
                score=dbot_score_int,
                integration_name="GreyNoise",
                malicious_description=malicious_description,
                reliability=reliability,
            )

            ip_standard_context = Common.IP(ip=response.get("address"), dbot_score=dbot_score)

            command_results.append(
                CommandResults(
                    readable_output=human_readable,
                    outputs_prefix="GreyNoise.IP",
                    outputs_key_field="address",
                    outputs=riot_response,
                    indicator=ip_standard_context,
                    raw_response=original_response,
                )
            )

        if response["seen"] and riot_response["riot"]:
            combo_response = response.copy()
            combo_response.update(riot_response)
            dbot_score_int, dbot_score_string = get_ip_reputation_score(response.get("classification"))

            human_readable = f"### IP: {ip} found with Reputation: {dbot_score_string}\n"
            human_readable += tableToMarkdown(
                name="GreyNoise Internet Scanner Intelligence Lookup", t=tmp_response, headers=IP_CONTEXT_HEADERS, removeNull=True
            )

            dbot_score = Common.DBotScore(
                indicator=response.get("address"),
                indicator_type=DBotScoreType.IP,
                score=dbot_score_int,
                integration_name="GreyNoise",
                malicious_description=malicious_description,
                reliability=reliability,
            )

            city = response.get("metadata", {}).get("source_city", "")
            region = response.get("metadata", {}).get("region", "")
            country_code = response.get("metadata", {}).get("source_country_code", "")
            geo_description = (
                f"City: {city}, Region: {region}, Country Code: {country_code}" if (city or region or country_code) else ""
            )
            ip_standard_context = Common.IP(
                ip=response.get("address"),
                asn=response.get("metadata", {}).get("asn"),
                hostname=response.get("actor"),
                geo_country=response.get("metadata", {}).get("source_country"),
                geo_description=geo_description,
                dbot_score=dbot_score,
            )

            riot_tmp_response = {
                "IP": f"[{riot_response.get('address')}](https://viz.greynoise.io/ip/{riot_response.get('address')})",
                "Business Service": riot_response.get("riot"),
                "Name": riot_response.get("name"),
                "Category": riot_response.get("category"),
                "Trust Level": riot_response.get("trust_level"),
                "Description": riot_response.get("description"),
                "Last Updated": riot_response.get("last_updated"),
            }

            human_readable += f"### IP: {ip} found with Reputation: {dbot_score_string}\n"
            human_readable += f"#### Belongs to Common Business Service: {riot_response.get('name', 'Unknown')}\n"
            human_readable += tableToMarkdown(
                name="GreyNoise Business Service Intelligence Lookup", t=riot_tmp_response, headers=RIOT_HEADERS, removeNull=False
            )

            command_results.append(
                CommandResults(
                    readable_output=human_readable,
                    outputs_prefix="GreyNoise.IP",
                    outputs_key_field="address",
                    outputs=combo_response,
                    indicator=ip_standard_context,
                    raw_response=original_response,
                )
            )

        if not response["seen"] and not riot_response["riot"]:
            combo_response = response.copy()
            combo_response.update(riot_response)
            combo_tmp_response = {
                "IP": combo_response.get("address"),
                "Business Service": combo_response.get("riot"),
                "Internet Scanner": combo_response.get("seen"),
            }

            dbot_score_int, dbot_score_string = get_ip_reputation_score(combo_response.get("classification"))

            dbot_score = Common.DBotScore(
                indicator=combo_response.get("address"),
                indicator_type=DBotScoreType.IP,
                score=dbot_score_int,
                integration_name="GreyNoise",
                malicious_description=malicious_description,
                reliability=reliability,
            )

            ip_standard_context = Common.IP(ip=response.get("address"), dbot_score=dbot_score)

            human_readable = f"### IP: {ip} No Mass-Internet Scanning Observed\n"
            human_readable += tableToMarkdown(
                name="GreyNoise Internet Scanner Intelligence Lookup",
                t=combo_tmp_response,
                headers=["IP", "Internet Scanner"],
                removeNull=True,
            )

            human_readable += f"### IP: {ip} Not Associated with a Business Service\n"
            human_readable += tableToMarkdown(
                name="GreyNoise Business Service Intelligence Lookup",
                t=combo_tmp_response,
                headers=["IP", "Business Service"],
                removeNull=True,
            )

            command_results.append(
                CommandResults(
                    readable_output=human_readable,
                    outputs_prefix="GreyNoise.IP",
                    outputs_key_field="address",
                    indicator=ip_standard_context,
                    outputs=combo_response,
                    raw_response=original_response,
                )
            )

    return command_results


@exception_handler
@logger
def query_command(client: Client, args: dict) -> CommandResults:
    """Get the information of IP based on the providence filters.

    :type client: ``Client``
    :param client: Client object for interaction with GreyNoise.

    :type args: ``dict``
    :param args: All command arguments, usually passed from ``demisto.args()``.

    :return: ``CommandResults`` object, that contains the IP information.
    :rtype: ``CommandResults``
    """
    advanced_query = generate_advanced_query(args)

    try:
        demisto.debug(f"Querying GreyNoise with query: {advanced_query}")
        query_response = client.query(query=advanced_query, size=args.get("size", "10"), scroll=args.get("next_token"))
    except Exception as e:
        demisto.debug(f"Error in query_command: {e}")
        raise DemistoException(EXCEPTION_MESSAGES["INVALID_RESPONSE"].format(e))

    if query_response["request_metadata"].get("message") not in ["ok", "No results. ", ""]:
        raise DemistoException(
            EXCEPTION_MESSAGES["QUERY_STATS_RESPONSE"].format(query_response["request_metadata"].get("message"))
        )

    original_response = copy.deepcopy(query_response)

    if query_response["request_metadata"]["message"] == "ok" or query_response["request_metadata"]["message"] == "":
        tmp_response = []
        for each in query_response.get("data", []):
            scanner_response = each["internet_scanner_intelligence"]
            scanner_response["seen"] = scanner_response.get("found", False)
            scanner_response["address"] = each["ip"]
            scanner_response["ip"] = each["ip"]
            tmp_response += get_ip_context_data([scanner_response])

        human_readable = "### GreyNoise Internet Scanner Intelligence\n"
        human_readable += f"#### Total findings: {query_response['request_metadata'].get('count')}\n"
        human_readable += f"#### Query: {query_response['request_metadata'].get('adjusted_query')}\n"

        human_readable += tableToMarkdown(
            name="GreyNoise Internet Scanner Intelligence", t=tmp_response, headers=IP_CONTEXT_HEADERS, removeNull=True
        )

        if not query_response.get("request_metadata", {}).get("complete"):
            human_readable += f"\n### Next Page Token: \n{query_response['request_metadata'].get('scroll')}"

        query = query_response.get("request_metadata", {}).get("adjusted_query", "").replace(" ", "+")
        query_link = f"https://viz.greynoise.io/query/?gnql={query}"
        query_link = query_link.replace("*", "&ast;")
        query_link = query_link.replace('"', "&quot;")
        human_readable += f"\n*To view the detailed query result please click [here]({query_link}).*"

        outputs = {
            QUERY_OUTPUT_PREFIX["IP"]: query_response.get("data", []),
            QUERY_OUTPUT_PREFIX["QUERY"]: {
                "complete": query_response.get("request_metadata", {}).get("complete"),
                "count": query_response.get("request_metadata", {}).get("count"),
                "message": query_response.get("request_metadata", {}).get("message"),
                "query": query_response.get("request_metadata", {}).get("adjusted_query"),
                "scroll": query_response.get("request_metadata", {}).get("scroll"),
            },
        }
    elif query_response["request_metadata"]["message"] == "No results. ":
        outputs = {}
        human_readable = "### GreyNoise Query returned No Results."
        query = query_response["request_metadata"].get("adjusted_query", "").replace(" ", "+")
        query_link = f"https://viz.greynoise.io/query/?gnql={query}"
        query_link = query_link.replace("*", "&ast;")
        query_link = query_link.replace('"', "&quot;")
        human_readable += f"\n*To view the detailed query result please click [here]({query_link}).*"
    else:
        outputs = {}
        human_readable = ""
        demisto.debug(
            f'{query_response["request_metadata"]["message"]=} does not match any condition. {outputs=} , {human_readable=}'
        )

    return CommandResults(readable_output=human_readable, outputs=remove_empty_elements(outputs), raw_response=original_response)


@exception_handler
@logger
def stats_command(client: Client, args: dict) -> Any:
    """Get aggregate statistics for the top organizations, actors, tags, ASNs, countries,
    classifications, and operating systems of all the results of a given GNQL query.

       :type client: ``Client``
       :param client: Client object for interaction with GreyNoise.

       :type args: ``dict``
       :param args: All command arguments, usually passed from ``demisto.args()``.

       :return: A ``CommandResults`` object that is then passed to ``return_results``,
           that contains the IP information.
       :rtype: ``CommandResults``
    """
    advanced_query = generate_advanced_query(args)
    try:
        demisto.debug(f"Querying GreyNoise with stats query: {advanced_query}")
        response = client.stats(query=advanced_query, count=args.get("size", "10"))
    except Exception as e:
        demisto.debug(f"Error in stats_command: {e}")
        raise DemistoException(EXCEPTION_MESSAGES["INVALID_RESPONSE"].format(e))

    if response["count"] > 0:
        human_readable = "### GreyNoise Internet Scanner Intelligence\n"
        human_readable += f"#### Stats Query: {response.get('adjusted_query')}\n"
        human_readable += f"#### Total IP Count: {response.get('count', '0')}\n"

        for key, value in response.get("stats", {}).items():
            hr_list: list = []
            if value is None:
                continue
            if key == "countries":
                continue
            for rec in value:
                hr_rec: dict = {}
                header = []
                for k, v in rec.items():
                    hr_rec.update({f"{STATS_H_KEY.get(k)}": f"{v}"})
                    header.append(STATS_H_KEY.get(k))
                hr_list.append(hr_rec)
            human_readable += tableToMarkdown(name=f"{STATS_KEY.get(key, key)}", t=hr_list, headers=header, removeNull=True)
    elif response.get("count") == 0:
        human_readable = "### GreyNoise Stats Query returned No Results."

    return CommandResults(
        outputs_prefix="GreyNoise.Stats",
        outputs_key_field="query",
        outputs=remove_empty_elements(response),
        readable_output=human_readable,
        raw_response=response,
    )


@exception_handler
@logger
def similarity_command(client: Client, args: dict) -> Any:
    """Get similarity information for a specified IP.

    :type client: ``Client``
    :param client: Client object for interaction with GreyNoise.

    :type args: ``dict``
    :param args: All command arguments, usually passed from ``demisto.args()``.

    :return: A ``CommandResults`` object that is then passed to ``return_results``,
        that contains the IP information.
    :rtype: ``CommandResults``
    """
    ip = args.get("ip", "")
    min_score = args.get("minimum_score", 90)
    limit = args.get("maximum_results", 50)
    if isinstance(min_score, str):
        min_score = int(min_score)
    if isinstance(limit, str):
        limit = int(limit)
    response = client.similar(ip, min_score=min_score, limit=limit)
    original_response = copy.deepcopy(response)
    response = remove_empty_elements(response)
    if not isinstance(response, dict):
        raise DemistoException(EXCEPTION_MESSAGES["INVALID_RESPONSE"].format(response))

    if response.get("similar_ips"):
        tmp_response = []
        for sim_ip in response.get("similar_ips", []):
            modified_sim_ip = copy.deepcopy(sim_ip)
            modified_sim_ip["IP"] = sim_ip.get("ip")
            modified_sim_ip["Score"] = sim_ip.get("score", "0") * 100
            modified_sim_ip["Classification"] = sim_ip.get("classification")
            modified_sim_ip["Actor"] = sim_ip.get("actor")
            modified_sim_ip["Organization"] = sim_ip.get("organization")
            modified_sim_ip["Source Country"] = sim_ip.get("source_country")
            modified_sim_ip["Last Seen"] = sim_ip.get("last_seen")
            modified_sim_ip["Similarity Features"] = sim_ip.get("features")
            tmp_response.append(modified_sim_ip)

        human_readable = f"### IP: {ip} - Similar Internet Scanners found in GreyNoise\n"
        human_readable += f"#### Total Similar IPs with Score above {min_score}%: {response.get('total')}\n"
        if response.get("total", 0) > limit:
            human_readable += f"##### Displaying {limit} results below.  To see all results, visit the GreyNoise Visualizer.\n "

        human_readable += tableToMarkdown(name="GreyNoise Similar IPs", t=tmp_response, headers=SIMILAR_HEADERS, removeNull=True)

        similarity_link = f"https://viz.greynoise.io/ip-similarity/{ip}"
        human_readable += f"\n*To view the detailed similarity result please click [here]({similarity_link}).*"

    elif response["message"] == "ip not found":
        human_readable = "### GreyNoise Similarity Lookup returned No Results."
        viz_link = f"https://viz.greynoise.io/ip/{ip}"
        human_readable += f"\n*To view this IP on the GreyNoise Visualizer please click [here]({viz_link}).*"

    return CommandResults(
        outputs_prefix="GreyNoise.Similar",
        outputs_key_field="ip",
        readable_output=human_readable,
        outputs=remove_empty_elements(response),
        raw_response=original_response,
    )


@exception_handler
@logger
def timeline_command(client: Client, args: dict) -> Any:
    """Get timeline information for a specified IP.

    :type client: ``Client``
    :param client: Client object for interaction with GreyNoise.

    :type args: ``dict``
    :param args: All command arguments, usually passed from ``demisto.args()``.

    :return: A ``CommandResults`` object that is then passed to ``return_results``,
        that contains the IP information.
    :rtype: ``CommandResults``
    """
    ip = args.get("ip", "")
    days = args.get("days", 30)
    limit = args.get("maximum_results", 50)
    if isinstance(days, str):
        days = int(days)
    if isinstance(limit, str):
        limit = int(limit)
    response = client.timelinedaily(ip, days=days, limit=limit)
    original_response = copy.deepcopy(response)
    response = remove_empty_elements(response)
    if not isinstance(response, dict):
        raise DemistoException(EXCEPTION_MESSAGES["INVALID_RESPONSE"].format(response))

    if response.get("activity"):
        tmp_response = []
        for activity in response.get("activity", []):
            modified_activity = copy.deepcopy(activity)
            modified_activity["Date"] = activity.get("timestamp").split("T")[0]
            modified_activity["Classification"] = activity.get("classification")
            tag_names = [tag["name"] for tag in activity.get("tags", [])]
            modified_activity["Tags"] = tag_names
            modified_activity["rDNS"] = activity.get("rdns")
            modified_activity["Organization"] = activity.get("organization")
            modified_activity["ASN"] = activity.get("asn")
            ports = [str(item["port"]) + "/" + str(item["transport_protocol"]) for item in activity.get("protocols", [])]
            modified_activity["Ports"] = ports
            modified_activity["Web Paths"] = activity.get("http_paths")
            modified_activity["User Agents"] = activity.get("http_user_agents")
            tmp_response.append(modified_activity)

        human_readable = f"### IP: {ip} - GreyNoise IP Timeline\n"

        human_readable += tableToMarkdown(
            name="Internet Scanner Timeline Details - Daily Activity Summary",
            t=tmp_response,
            headers=TIMELINE_HEADERS,
            removeNull=True,
        )

        timeline_link = f"https://viz.greynoise.io/ip/{ip}?view=timeline"
        human_readable += f"\n*To view the detailed timeline result please click [here]({timeline_link}).*"

    else:
        human_readable = "### GreyNoise IP Timeline Returned No Results."
        viz_link = f"https://viz.greynoise.io/ip/{ip}"
        human_readable += f"\n*To view this IP on the GreyNoise Visualizer please click [here]({viz_link}).*"

    return CommandResults(
        outputs_prefix="GreyNoise.Timeline",
        outputs_key_field="ip",
        readable_output=human_readable,
        outputs=remove_empty_elements(response),
        raw_response=original_response,
    )


@exception_handler
@logger
def riot_command(client: Client, args: dict, reliability: str) -> CommandResults:
    """
    Returns information about IP whether it is harmful or not. RIOT (Rule It Out) means to inform the analyst about
    the harmfulness of the IP. For the harmless IP, the value of Riot is "True" which in turn returns DNS and other
    information about the IP. For the harmful IP, the value of Riot is "False".

    :type client: ``Client``
    :param client: client object

    :type args: ``dict``
    :param args: All command arguments, usually passed from ``demisto.args()``.
    :return: A ``CommandResults`` object that is then passed to ``return_results``,
           that contains the IP information.
    :rtype: ``CommandResults``

    :type reliability: ``String``
    :param reliability: string
    """
    ip = args["ip"]
    try:
        demisto.debug(f"Querying GreyNoise with ip: {ip}")
        api_response = client.ip(ip)
    except Exception as e:
        demisto.debug(f"Error in riot_command: {e}")
        raise DemistoException(EXCEPTION_MESSAGES["INVALID_RESPONSE"].format(e))

    response = api_response.get("business_service_intelligence", {})
    response["ip"] = api_response.get("ip", "")
    response["riot"] = response.get("found", False)
    original_response = copy.deepcopy(api_response)
    response = remove_empty_elements(response)
    name = ""
    if response.get("riot") is False or response.get("riot") == "false":
        name = "GreyNoise Business Service Intelligence Lookup"
        hr = {
            "IP": response.get("ip"),
            "Business Service": response.get("riot"),
        }
        human_readable = f"### IP: {ip} Not Associated with a Business Service\n"
        human_readable += tableToMarkdown(name=name, t=hr, headers=["IP", "Business Service"], removeNull=False)
        dbot_score_int, dbot_score_string = get_ip_reputation_score(response.get("classification"))
    elif response.get("riot") is True or response.get("riot") == "true":
        if response.get("logo_url", "") != "":
            del response["logo_url"]
        if response.get("trust_level") == "1":
            response["trust_level"] = "1 - Reasonably Ignore"
            response["classification"] = "benign"
        elif response.get("trust_level") == "2":  # Commonly Seen
            response["trust_level"] = "2 - Commonly Seen"
            response["classification"] = "unknown"
        elif response.get("trust_level") == "3":  # Context Only
            response["trust_level"] = "3 - Context Only"
            response["classification"] = "unknown"
        dbot_score_int, dbot_score_string = get_ip_reputation_score(response.get("classification"))
        name = "GreyNoise Business Service Intelligence Lookup"
        hr = {
            "IP": f"[{response.get('ip')}](https://viz.greynoise.io/ip/{response.get('ip')})",
            "Business Service": response.get("riot"),
            "Name": response.get("name"),
            "Category": response.get("category"),
            "Trust Level": response.get("trust_level"),
            "Description": response.get("description"),
            "Last Updated": response.get("last_updated"),
        }
        headers = RIOT_HEADERS

        human_readable = f"### IP: {ip} found with Reputation: {dbot_score_string}\n"
        human_readable += f"#### Belongs to Common Business Service: {response['name']}\n"
        human_readable += tableToMarkdown(name=name, t=hr, headers=headers, removeNull=False)
    else:
        dbot_score_int = 0
        demisto.debug(f'{response.get("riot")=} -> {dbot_score_int=}')

    dbot_score = Common.DBotScore(
        indicator=response.get("ip"),
        indicator_type=DBotScoreType.IP,
        score=dbot_score_int,
        integration_name="GreyNoise",
        reliability=reliability,
    )

    ip_standard_context = Common.IP(ip=response.get("ip"), dbot_score=dbot_score)

    return CommandResults(
        outputs_prefix="GreyNoise.IP",
        outputs_key_field="ip",
        outputs=response,
        readable_output=human_readable,
        indicator=ip_standard_context,
        raw_response=original_response,
    )


@exception_handler
@logger
def context_command(client: Client, args: dict, reliability: str) -> CommandResults:
    """
    Returns information about IP whether it is harmful or not. RIOT (Rule It Out) means to inform the analyst about
    the harmfulness of the IP. For the harmless IP, the value of Riot is "True" which in turn returns DNS and other
    information about the IP. For the harmful IP, the value of Riot is "False".

    :type client: ``Client``
    :param client: client object

    :type args: ``dict``
    :param args: All command arguments, usually passed from ``demisto.args()``.
    :return: A ``CommandResults`` object that is then passed to ``return_results``,
           that contains the IP information.
    :rtype: ``CommandResults``

    :type reliability: ``String``
    :param reliability: string
    """

    ip = args.get("ip", "")
    try:
        demisto.debug(f"Querying GreyNoise with ip: {ip}")
        api_response = client.ip(ip)
    except Exception as e:
        demisto.debug(f"Error in context_command: {e}")
        raise DemistoException(EXCEPTION_MESSAGES["INVALID_RESPONSE"].format(e))

    if "internet_scanner_intelligence" in api_response:
        response = api_response.get("internet_scanner_intelligence", {})
        response["seen"] = response.get("found", False)
        response["address"] = api_response.get("ip", "")
        response["ip"] = api_response.get("ip", "")
    else:
        response = {"found": False, "address": api_response.get("ip", ""), "seen": False, "ip": api_response.get("ip", "")}

    original_response = copy.deepcopy(api_response)

    response = remove_empty_elements(response)

    dbot_score_int, dbot_score_string = get_ip_reputation_score(response.get("classification"))

    if response["found"]:
        tmp_response = get_ip_context_data([response])
        human_readable = f"### IP: {ip} found with Reputation: {dbot_score_string}\n"
        headers = IP_CONTEXT_HEADERS
    else:
        human_readable = f"### IP: {ip} No Mass-Internet Scanning Observed\n"
        tmp_response = [{"IP": response.get("address"), "Internet Scanner": response.get("found")}]
        headers = ["IP", "Internet Scanner"]

    human_readable += tableToMarkdown(
        name="GreyNoise Internet Scanner Intelligence Lookup", t=tmp_response, headers=headers, removeNull=True
    )

    if response["found"] and response["classification"] == "malicious":
        malicious_description = "This IP has been observed scanning the internet in a malicious manner."
    else:
        malicious_description = ""

    dbot_score = Common.DBotScore(
        indicator=response.get("address"),
        indicator_type=DBotScoreType.IP,
        score=dbot_score_int,
        integration_name="GreyNoise",
        malicious_description=malicious_description,
        reliability=reliability,
    )

    city = response.get("metadata", {}).get("source_city", "")
    region = response.get("metadata", {}).get("region", "")
    country_code = response.get("metadata", {}).get("source_country_code", "")
    geo_description = f"City: {city}, Region: {region}, Country Code: {country_code}" if (city or region or country_code) else ""

    ip_standard_context = Common.IP(
        ip=response.get("address"),
        asn=response.get("metadata", {}).get("asn"),
        hostname=response.get("actor"),
        geo_country=response.get("metadata", {}).get("source_country"),
        geo_description=geo_description,
        dbot_score=dbot_score,
    )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="GreyNoise.IP",
        outputs_key_field="address",
        outputs=response,
        indicator=ip_standard_context,
        raw_response=original_response,
    )


@exception_handler
@logger
def cve_command(client: Client, args: dict, reliability: str) -> List[CommandResults]:
    """
    Returns information about CVE.

    :type client: ``Client``
    :param client: client object

    :type reliability: ``String``
    :param reliability: string

    :type args: ``dict``
    :param args: All command arguments, usually passed from ``demisto.args()``.
    :return: A ``CommandResults`` object that is then passed to ``return_results``,
           that contains the IP information.
    :rtype: ``CommandResults``

    """
    cve_list = argToList(args.get("cve"), ",")
    command_results = []
    reliability = reliability if reliability else DBotScoreReliability.B
    for cve_arg in cve_list:
        cvss = 0
        description = ""
        published = ""
        modified = ""
        response = client.cve(cve_arg)
        cve_raw_response = copy.deepcopy(response)
        response = remove_empty_elements(response)
        if isinstance(response, dict) and response.get("id"):
            cvss = response["details"].get("cve_cvss_score", "")
            description = response["details"].get("vulnerability_description", "")
            vendor = response["details"].get("vendor", "")
            product = response["details"].get("product", "")
            if "timeline" in response:
                published = response["timeline"].get("cve_published_date", "").split("T")[0]
                modified = response["timeline"].get("cve_last_updated_date", "").split("T")[0]
            name = "GreyNoise CVE Lookup"
            hr = {
                "CVE ID": response.get("id"),
                "CVSS": cvss,
                "Vendor": vendor,
                "Product": product,
                "Published to NVD": response["details"].get("published_to_nist_nvd", False),
            }
            human_readable = f"### CVE: {cve_arg} is found\n"
            human_readable += tableToMarkdown(
                name=name, t=hr, headers=["CVE ID", "CVSS", "Vendor", "Product", "Published to NVD"], removeNull=False
            )
            if "timeline" in response:
                name = "Timeline Details"
                hr = {
                    "Added to Kev": response["timeline"].get("cisa_kev_date_added", "").split("T")[0],
                    "Last Updated": modified,
                    "CVE Published": published,
                    "First Published": response["timeline"].get("first_known_published_date", "").split("T")[0],
                }
                human_readable += tableToMarkdown(
                    name=name,
                    t=hr,
                    headers=["Added to Kev", "Last Updated", "CVE Published", "First Published"],
                    removeNull=False,
                )
            if "exploitation_details" in response:
                name = "Exploitation Details"
                hr = {
                    "Attack Vector": response["exploitation_details"].get("attack_vector", ""),
                    "EPSS Base Score": response["exploitation_details"].get("epss_score", ""),
                    "Exploit Found": response["exploitation_details"].get("exploit_found", ""),
                    "Exploit Registered in KEV": response["exploitation_details"].get("exploitation_registered_in_kev", ""),
                }
                human_readable += tableToMarkdown(
                    name=name,
                    t=hr,
                    headers=["Attack Vector", "EPSS Base Score", "Exploit Found", "Exploit Registered in KEV"],
                    removeNull=False,
                )
            if "exploitation_stats" in response:
                name = "Exploitation Stats"
                hr = {
                    "# of Available Exploits": response["exploitation_stats"].get("number_of_available_exploits", ""),
                    "# of Botnets Exploiting": response["exploitation_stats"].get(
                        "number_of_botnets_exploiting_vulnerability", ""
                    ),
                    "# of Threat Actors Exploiting": response["exploitation_stats"].get(
                        "number_of_threat_actors_exploiting_vulnerability", ""
                    ),
                }
                human_readable += tableToMarkdown(
                    name=name,
                    t=hr,
                    headers=["# of Available Exploits", "# of Botnets Exploiting", "# of Threat Actors Exploiting"],
                    removeNull=False,
                )
            if "exploitation_activity" in response:
                name = "Exploitation Activity - GreyNoise Insights"
                hr = {
                    "GreyNoise Observed Activity": response["exploitation_activity"].get("activity_seen", ""),
                    "# of Benign IPs - Last Day": response["exploitation_activity"].get("benign_ip_count_1d", ""),
                    "# of Benign IPs - Last 10 Days": response["exploitation_activity"].get("benign_ip_count_10d", ""),
                    "# of Benign IPs - Last 30 Days": response["exploitation_activity"].get("benign_ip_count_30d", ""),
                    "# of Threat IPs - Last Day": response["exploitation_activity"].get("threat_ip_count_1d", ""),
                    "# of Threat IPs - Last 10 Days": response["exploitation_activity"].get("threat_ip_count_10d", ""),
                    "# of Threat IPs - Last 30 Days": response["exploitation_activity"].get("threat_ip_count_30d", ""),
                }
                human_readable += tableToMarkdown(
                    name=name,
                    t=hr,
                    headers=[
                        "GreyNoise Observed Activity",
                        "# of Benign IPs - Last Day",
                        "# of Benign IPs - Last 10 Days",
                        "# of Benign IPs - Last 30 Days",
                        "# of Threat IPs - Last Day",
                        "# of Threat IPs - Last 10 Days",
                        "# of Threat IPs - Last 30 Days",
                    ],
                    removeNull=False,
                )
        else:
            name = "GreyNoise CVE IP Lookup"
            hr = {
                "CVE ID": cve_arg,
            }
            human_readable = f"### CVE: {cve_arg} is not found\n"
            human_readable += tableToMarkdown(name=name, t=hr, headers=["CVE ID"], removeNull=False)

        dbot_score = Common.DBotScore(
            indicator=cve_arg,
            indicator_type=DBotScoreType.CVE,
            score=Common.DBotScore.NONE,
            integration_name="GreyNoise",
            reliability=reliability,
        )
        cve = Common.CVE(
            id=cve_arg,
            cvss=cvss,
            description=description,
            published=published,
            modified=modified,
            dbot_score=dbot_score,
        )

        command_results.append(
            CommandResults(
                outputs_prefix="GreyNoise.CVE",
                outputs_key_field="id",
                outputs=cve_raw_response,
                indicator=cve,
                readable_output=human_readable,
            )
        )

    return command_results


""" MAIN FUNCTION """


def main() -> None:
    """main function, parses params and runs command functions

    :return:
    :rtype:
    """
    # get pack version - use default if any step fails
    pack_version = "2.1.0"
    try:
        pack_version = get_pack_version()
    except Exception as e:
        demisto.debug(f"Failed to retrieve pack version, using default: {str(e)}")

    api_key = demisto.params().get("credentials", {}).get("password") or demisto.params().get("apikey")
    if not api_key:
        return_error("Please provide a valid API token")
    proxy = demisto.params().get("proxy", False)
    reliability = demisto.params().get("integrationReliability", "B - Usually reliable")
    reliability = reliability if reliability else DBotScoreReliability.B
    if DBotScoreReliability.is_valid_type(reliability):
        reliability = DBotScoreReliability.get_dbot_score_reliability_from_str(reliability)
    else:
        Exception("Please provide a valid value for the Integration Reliability parameter.")

    demisto.debug(f"Command being called is {demisto.command()}")
    try:
        api_config = APIConfig(
            api_key=api_key,
            api_server=API_SERVER,
            timeout=TIMEOUT,
            proxy=handle_proxy("proxy", proxy).get("https", ""),
            use_cache=False,
            integration_name=f"xsoar-integration-v{pack_version}",
        )
        client = Client(api_config)

        if demisto.command() == "test-module":
            # This is the call made when pressing the integration Test button.
            result: Any = test_module(client)
            return_results(result)

        elif demisto.command() == "greynoise-ip-quick-check":
            result = ip_quick_check_command(client, demisto.args())
            return_results(result)

        elif demisto.command() == "ip":
            result = ip_reputation_command(client, demisto.args(), reliability)
            return_results(result)

        elif demisto.command() == "greynoise-stats":
            result = stats_command(client, demisto.args())
            return_results(result)

        elif demisto.command() == "greynoise-similarity":
            result = similarity_command(client, demisto.args())
            return_results(result)

        elif demisto.command() == "greynoise-timeline":
            result = timeline_command(client, demisto.args())
            return_results(result)

        elif demisto.command() == "greynoise-query":
            result = query_command(client, demisto.args())
            return_results(result)

        elif demisto.command() == "greynoise-riot":
            result = riot_command(client, demisto.args(), reliability)
            return_results(result)

        elif demisto.command() == "greynoise-context":
            result = context_command(client, demisto.args(), reliability)
            return_results(result)

        elif demisto.command() == "cve":
            result = cve_command(client, demisto.args(), reliability)
            return_results(result)
    # Log exceptions and return errors
    except DemistoException as err:
        return_error(str(err))

    except Exception as err:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(EXCEPTION_MESSAGES["COMMAND_FAIL"].format(demisto.command(), str(err)))


""" ENTRY POINT """

if __name__ in ("__main__", "__builtin__", "builtins"):
    main()