PassiveTotal v2

Analyze and understand threat infrastructure from a variety of sources-passive DNS, active DNS, WHOIS, SSL certificates and more-without devoting resources to time-intensive manual threat research and analysis.

Data Enrichment & Threat Intelligence · PassiveTotal

Details

IDPassiveTotal v2
ProviderMicrosoft
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Analyze and understand threat infrastructure from a variety of sources-passive DNS, active DNS, WHOIS, SSL certificates and more-without devoting resources to time-intensive manual threat research and analysis.
This integration was integrated and tested with enterprise version of PassiveTotal v2.

Configure PassiveTotal v2 in Cortex

Parameter Description Required
url API URL True
username Username True
secret API Secret True
insecure Trust any certificate (not secure) False
proxy Use system proxy settings False
request_timeout HTTP(S) Request Timeout (in seconds) 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.

pt-whois-search


Gets WHOIS information records based on field matching queries.

Base Command

pt-whois-search

Input

Argument Name Description Required
query Query value to use in your request. Required
field WHOIS field to execute the search on: domain, email, name, organization, address, phone, nameserver. Required

Context Output

Path Type Description
Domain.Name String The domain name, for example: ‘google.com’.
Domain.WHOIS.CreationDate Date The date that the domain was created.
Domain.WHOIS.UpdatedDate Date The date that the domain was last updated.
Domain.WHOIS.ExpirationDate Date The expiration date of the domain.
Domain.WHOIS.NameServers String Name servers of the domain.
Domain.Organization String The organization of the domain.
Domain.Admin.Email String The email address of the domain administrator.
Domain.Admin.Name String The name of the domain administrator.
Domain.Admin.Phone String The phone number of the domain administrator.
Domain.Admin.Country String The country of the domain administrator.
Domain.Registrant.Email String The email address of the registrant.
Domain.Registrant.Name String The name of the registrant.
Domain.Registrant.Phone String The phone number for receiving abuse reports.
Domain.Registrant.Country String The country of the registrant.
Domain.WHOIS.Admin.Email String The email address of the domain administrator.
Domain.WHOIS.Admin.Name String The name of the domain administrator.
Domain.WHOIS.Admin.Phone String The phone number of the domain administrator.
Domain.WHOIS.Admin.Country String The country of the domain administrator.
Domain.WHOIS.Registrar.Name String The name of the registrar, for example: ‘GoDaddy’.
Domain.WHOIS.Registrant.Email String The email address of the registrant.
Domain.WHOIS.Registrant.Name String The name of the registrant.
Domain.WHOIS.Registrant.Phone String The phone number for receiving abuse reports.
Domain.WHOIS.Registrant.Country String The country of the registrant.
PassiveTotal.WHOIS.domain String The domain name, for example: ‘google.com’.
PassiveTotal.WHOIS.registrar String The name of the registrar of the domain
PassiveTotal.WHOIS.whoisServer String WHOIS server name where the details of domain registrations belong
PassiveTotal.WHOIS.registered Date The date that the domain was registered.
PassiveTotal.WHOIS.expiresAt Date The expiration date of the domain.
PassiveTotal.WHOIS.registryUpdatedAt Date The date when registry was last updated.
PassiveTotal.WHOIS.lastLoadedAt Date Last loaded date of WHOIS database.
PassiveTotal.WHOIS.nameServers String Name servers of the domain.
PassiveTotal.WHOIS.organization String The organization of the domain.
PassiveTotal.WHOIS.name String Name of the domain.
PassiveTotal.WHOIS.telephone String Telephone number fetched from whois details of the domain.
PassiveTotal.WHOIS.contactEmail String Contact Email address of the domain owner
PassiveTotal.WHOIS.registrantEmail String The name of the domain registrant.
PassiveTotal.WHOIS.registrantFax String The fax number of the domain registrant.
PassiveTotal.WHOIS.registrantName String The name of the domain registrant.
PassiveTotal.WHOIS.registrantOrganization String The organizations of the domain registrant.
PassiveTotal.WHOIS.registrantStreet String The street of the domain registrant.
PassiveTotal.WHOIS.registrantCity String The city of the domain registrant.
PassiveTotal.WHOIS.registrantState String The state of the domain registrant.
PassiveTotal.WHOIS.registrantPostalCode String The postal code of the domain registrant.
PassiveTotal.WHOIS.registrantCountry String The country of the domain registrant.
PassiveTotal.WHOIS.registrantTelephone String The telephone number of the domain registrant.
PassiveTotal.WHOIS.adminEmail String The email address of the domain administrator.
PassiveTotal.WHOIS.adminFax String The fax number of the domain administrator.
PassiveTotal.WHOIS.adminName String The name of the domain administrator.
PassiveTotal.WHOIS.adminOrganization String The organizations of the domain administrator.
PassiveTotal.WHOIS.adminStreet String The street of the domain administrator.
PassiveTotal.WHOIS.adminCity String The city of the domain administrator.
PassiveTotal.WHOIS.adminState String The state of the domain administrator.
PassiveTotal.WHOIS.adminPostalCode String The postal code of the domain administrator.
PassiveTotal.WHOIS.adminCountry String The country of the domain administrator.
PassiveTotal.WHOIS.adminTelephone String The telephone number of the domain administrator.
PassiveTotal.WHOIS.billingEmail String The email address of the domain billing.
PassiveTotal.WHOIS.billingFax String The fax number of the domain billing.
PassiveTotal.WHOIS.billingName String The name of the domain billing.
PassiveTotal.WHOIS.billingOrganization String The organizations of the domain billing.
PassiveTotal.WHOIS.billingStreet String The street of the domain billing.
PassiveTotal.WHOIS.billingCity String The city of the domain billing.
PassiveTotal.WHOIS.billingState String The state of the domain billing.
PassiveTotal.WHOIS.billingPostalCode String The postal code of the domain billing.
PassiveTotal.WHOIS.billingCountry String The country of the domain billing.
PassiveTotal.WHOIS.billingTelephone String The telephone number of the domain billing.
PassiveTotal.WHOIS.techEmail String The email address of the domain tech.
PassiveTotal.WHOIS.techFax String The fax number of the domain tech.
PassiveTotal.WHOIS.techName String The name of the domain tech.
PassiveTotal.WHOIS.techOrganization String The organizations of domain tech.
PassiveTotal.WHOIS.techStreet String The street of the domain tech.
PassiveTotal.WHOIS.techCity String The city of the domain tech.
PassiveTotal.WHOIS.techState String The state of the domain tech.
PassiveTotal.WHOIS.techPostalCode String The postal code of the domain tech.
PassiveTotal.WHOIS.techCountry String The country of the domain tech.
PassiveTotal.WHOIS.techTelephone String The telephone number of the domain tech.

Command Example

!pt-whois-search field=domain query=riskiq.com

Context Example

{
    "DBotScore": [
        {
            "Indicator": "riskiq.com",
            "Score": 0,
            "Type": "domain",
            "Vendor": "PassiveTotal"
        }
    ],
    "Domain": [
        {
            "Admin": {
                "Country": "us",
                "Email": "domains@riskiq.com",
                "Name": "Risk IQ",
                "Phone": "18884154447"
            },
            "CreationDate": "2006-01-11T16:00:00.000-0800",
            "ExpirationDate": "2017-01-11T16:00:00.000-0800",
            "Name": "riskiq.com",
            "NameServers": [
                "luke.ns.cloudflare.com",
                "serena.ns.cloudflare.com"
            ],
            "Organization": "RiskIQ, Inc.",
            "Registrant": {
                "Country": "us",
                "Email": "domains@riskiq.com",
                "Name": "Risk IQ",
                "Phone": "18884154447"
            },
            "Registrar": {
                "AbuseEmail": null,
                "AbusePhone": null,
                "Name": "GODADDY.COM, LLC"
            },
            "UpdatedDate": "2014-12-08T16:00:00.000-0800",
            "WHOIS": {
                "Admin": {
                    "Country": "us",
                    "Email": "domains@riskiq.com",
                    "Name": "Risk IQ",
                    "Phone": "18884154447"
                },
                "CreationDate": "2006-01-11T16:00:00.000-0800",
                "ExpirationDate": "2017-01-11T16:00:00.000-0800",
                "NameServers": [
                    "luke.ns.cloudflare.com",
                    "serena.ns.cloudflare.com"
                ],
                "Registrant": {
                    "Country": "us",
                    "Email": "domains@riskiq.com",
                    "Name": "Risk IQ",
                    "Phone": "18884154447"
                },
                "Registrar": {
                    "AbuseEmail": null,
                    "AbusePhone": null,
                    "Name": "GODADDY.COM, LLC"
                },
                "UpdatedDate": "2014-12-08T16:00:00.000-0800"
            }
        }
    ],
    "PassiveTotal": {
        "WHOIS": {
            "adminCity": "san francisco",
            "adminCountry": "us",
            "adminEmail": "domains@riskiq.com",
            "adminName": "Risk IQ",
            "adminOrganization": "RiskIQ, Inc.",
            "adminPostalCode": "94111",
            "adminState": "california",
            "adminStreet": "22 Battery Street\n10th Floor",
            "adminTelephone": "18884154447",
            "contactEmail": "domains@riskiq.com",
            "domain": "riskiq.com",
            "expiresAt": "2017-01-11T16:00:00.000-0800",
            "lastLoadedAt": "2016-09-27T09:40:31.180-0700",
            "name": "Risk IQ",
            "nameServers": [
                "luke.ns.cloudflare.com",
                "serena.ns.cloudflare.com"
            ],
            "organization": "RiskIQ, Inc.",
            "registered": "2006-01-11T16:00:00.000-0800",
            "registrantCity": "san francisco",
            "registrantCountry": "us",
            "registrantEmail": "domains@riskiq.com",
            "registrantName": "Risk IQ",
            "registrantOrganization": "RiskIQ, Inc.",
            "registrantPostalCode": "94111",
            "registrantState": "california",
            "registrantStreet": "22 Battery Street\n10th Floor",
            "registrantTelephone": "18884154447",
            "registrar": "GODADDY.COM, LLC",
            "registryUpdatedAt": "2014-12-08T16:00:00.000-0800",
            "techCity": "san francisco",
            "techCountry": "us",
            "techEmail": "domains@riskiq.com",
            "techName": "Risk IQ",
            "techOrganization": "RiskIQ, Inc.",
            "techPostalCode": "94111",
            "techState": "california",
            "techStreet": "22 Battery Street\n10th Floor",
            "techTelephone": "18884154447",
            "telephone": "18884154447",
            "whoisServer": "whois.godaddy.com"
        }
    }
}

Human Readable Output

Total Retrieved Record(s): 2

Associated Domains

Domain WHOIS Server Registrar Contact Email Name Servers Registrant Admin Tech Creation Date (GMT) Expire Date (GMT) Updated Date (GMT) Last Scanned (GMT)
riskiq.com whois.godaddy.com GODADDY.COM, LLC domains@riskiq.com luke.ns.cloudflare.com, serena.ns.cloudflare.com City: san francisco,
Country: us,
Email: domains@riskiq.com,
Name: Risk IQ,
Organization: RiskIQ, Inc.,
PostalCode: 94111,
State: california,
Street: 22 Battery Street
10th Floor,
Telephone: 18884154447
City: san francisco,
Country: us,
Email: domains@riskiq.com,
Name: Risk IQ,
Organization: RiskIQ, Inc.,
PostalCode: 94111,
State: california,
Street: 22 Battery Street
10th Floor,
Telephone: 18884154447
City: san francisco,
Country: us,
Email: domains@riskiq.com,
Name: Risk IQ,
Organization: RiskIQ, Inc.,
PostalCode: 94111,
State: california,
Street: 22 Battery Street
10th Floor,
Telephone: 18884154447
2006-01-11T16:00:00.000-0800 2017-01-11T16:00:00.000-0800 2014-12-08T16:00:00.000-0800 2016-09-27T09:40:31.180-0700

pt-get-components


Retrieves the host attribute components for a domain or IP address. Maximum 2000 records are fetched.

Base Command

pt-get-components

Input

Argument Name Description Required
query Domain or IP address you want to search components for. Required
start Filter for records whose last seen is after this datetime. It accepts “yyyy-mm-dd hh:mm:ss” or “yyyy-mm-dd” format. Optional
end Filter for records whose first seen is before this datetime. It accepts “yyyy-mm-dd hh:mm:ss” or “yyyy-mm-dd” format. Optional

Context Output

Path Type Description
Domain.Name String The domain name, for example: “google.com”.
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.
IP.Address String The IP Address of the component.
PassiveTotal.Component.firstSeen Date The date and time when the component was first observed.
PassiveTotal.Component.lastSeen Date The date and time when the component was most recently observed.
PassiveTotal.Component.version String The current version of component.
PassiveTotal.Component.category String The category under which the component falls.
PassiveTotal.Component.label String The value of the component.
PassiveTotal.Component.hostname String The hostname of the component.
PassiveTotal.Component.address String The IP address of the component.

Command Example

!pt-get-components query=www.furth.com.ar

Context Example

{
    "DBotScore": {
        "Indicator": "www.furth.com.ar",
        "Score": 0,
        "Type": "domain",
        "Vendor": "PassiveTotal"
    },
    "Domain": {
        "Name": "www.furth.com.ar"
    },
    "PassiveTotal": {
        "Component": [
            {
                "category": "Framework",
                "firstSeen": "2020-05-29 10:57:44",
                "hostname": "www.furth.com.ar",
                "label": "PHP",
                "lastSeen": "2020-05-29 10:57:44"
            },
            {
                "category": "Server",
                "firstSeen": "2020-05-29 10:57:44",
                "hostname": "www.furth.com.ar",
                "label": "Apache",
                "lastSeen": "2020-05-29 10:57:44"
            },
            {
                "category": "Server Module",
                "firstSeen": "2016-01-11 23:45:15",
                "hostname": "www.furth.com.ar",
                "label": "mod_bwlimited",
                "lastSeen": "2017-10-24 15:53:52",
                "version": "1.4"
            },
            {
                "category": "Server Module",
                "firstSeen": "2016-01-11 23:45:15",
                "hostname": "www.furth.com.ar",
                "label": "OpenSSL",
                "lastSeen": "2017-10-24 15:53:52",
                "version": "1.0.1e-fips"
            },
            {
                "category": "Server",
                "firstSeen": "2016-01-11 23:45:15",
                "hostname": "www.furth.com.ar",
                "label": "Apache",
                "lastSeen": "2017-10-24 15:53:52",
                "version": "2.2.29"
            },
            {
                "category": "Operating System",
                "firstSeen": "2016-01-11 23:45:15",
                "hostname": "www.furth.com.ar",
                "label": "Unix",
                "lastSeen": "2017-10-24 15:53:52"
            },
            {
                "category": "Server Module",
                "firstSeen": "2016-01-11 23:45:15",
                "hostname": "www.furth.com.ar",
                "label": "mod_ssl",
                "lastSeen": "2017-10-24 15:53:52",
                "version": "2.2.29"
            }
        ]
    }
}

Human Readable Output

Total Retrieved Record(s): 7

COMPONENTS

Hostname First (GMT) Last (GMT) Category Value Version
www.furth.com.ar 2020-05-29 10:57:44 2020-05-29 10:57:44 Framework PHP  
www.furth.com.ar 2020-05-29 10:57:44 2020-05-29 10:57:44 Server Apache  
www.furth.com.ar 2016-01-11 23:45:15 2017-10-24 15:53:52 Server Module mod_bwlimited 1.4
www.furth.com.ar 2016-01-11 23:45:15 2017-10-24 15:53:52 Server Module OpenSSL 1.0.1e-fips
www.furth.com.ar 2016-01-11 23:45:15 2017-10-24 15:53:52 Server Apache 2.2.29
www.furth.com.ar 2016-01-11 23:45:15 2017-10-24 15:53:52 Operating System Unix  
www.furth.com.ar 2016-01-11 23:45:15 2017-10-24 15:53:52 Server Module mod_ssl 2.2.29

pt-get-trackers


Retrieves the host attribute trackers for a domain or IP address. Maximum 2000 records are fetched.

Base Command

pt-get-trackers

Input

Argument Name Description Required
query Domain or IP address you want to search trackers for. Required
start Filter for records whose last seen is after this datetime. It accepts “yyyy-mm-dd hh:mm:ss” or “yyyy-mm-dd” format. Optional
end Filter for records whose first seen is before this datetime. It accepts “yyyy-mm-dd hh:mm:ss” or “yyyy-mm-dd” format. Optional

Context Output

Path Type Description
Domain.Name String The domain name, for example: “google.com”.
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.
IP.Address String The IP Address of the component.
PassiveTotal.Tracker.firstSeen Date The date and time when the tracker was first observed.
PassiveTotal.Tracker.lastSeen Date The date and time when the tracker was most recently observed.
PassiveTotal.Tracker.attributeValue String The value of the tracker.
PassiveTotal.Tracker.attributeType String The type under which the tracker falls.
PassiveTotal.Tracker.hostname String The hostname of the tracker.
PassiveTotal.Tracker.address String The IP address of the tracker.

Command Example

!pt-get-trackers query=filmesonlinegratis.net

Context Example

{
    "DBotScore": [
        {
            "Indicator": "filmesonlinegratis.net",
            "Score": 0,
            "Type": "domain",
            "Vendor": "PassiveTotal"
        },
        {
            "Indicator": "www.filmesonlinegratis.net",
            "Score": 0,
            "Type": "domain",
            "Vendor": "PassiveTotal"
        }
    ],
    "Domain": [
        {
            "Name": "filmesonlinegratis.net"
        },
        {
            "Name": "www.filmesonlinegratis.net"
        }
    ],
    "PassiveTotal": {
        "Tracker": [
            {
                "attributeType": "GoogleAnalyticsTrackingId",
                "attributeValue": "ua-70630818-3",
                "firstSeen": "2016-10-14 10:16:38",
                "hostname": "filmesonlinegratis.net",
                "lastSeen": "2020-06-14 19:43:28"
            },
            {
                "attributeType": "GoogleAnalyticsAccountNumber",
                "attributeValue": "ua-70630818",
                "firstSeen": "2016-10-14 10:16:38",
                "hostname": "filmesonlinegratis.net",
                "lastSeen": "2020-06-14 19:43:28"
            },
            {
                "attributeType": "GoogleAnalyticsAccountNumber",
                "attributeValue": "ua-11598035",
                "firstSeen": "2012-03-07 05:53:50",
                "hostname": "www.filmesonlinegratis.net",
                "lastSeen": "2016-10-13 15:38:35"
            },
            {
                "attributeType": "GoogleAnalyticsTrackingId",
                "attributeValue": "ua-11598035-1",
                "firstSeen": "2012-03-07 05:53:50",
                "hostname": "www.filmesonlinegratis.net",
                "lastSeen": "2016-10-13 15:38:35"
            },
            {
                "attributeType": "GoogleAnalyticsTrackingId",
                "attributeValue": "ua-11598035-1",
                "firstSeen": "2014-02-11 01:30:40",
                "hostname": "filmesonlinegratis.net",
                "lastSeen": "2016-09-13 03:54:34"
            },
            {
                "attributeType": "GoogleAnalyticsAccountNumber",
                "attributeValue": "ua-11598035",
                "firstSeen": "2014-02-11 01:30:40",
                "hostname": "filmesonlinegratis.net",
                "lastSeen": "2016-09-13 03:54:34"
            },
            {
                "attributeType": "TumblrId",
                "attributeValue": "25.media",
                "firstSeen": "2016-07-02 00:46:33",
                "hostname": "www.filmesonlinegratis.net",
                "lastSeen": "2016-09-02 11:09:30"
            },
            {
                "attributeType": "FacebookId",
                "attributeValue": "filmesog",
                "firstSeen": "2012-11-27 06:06:44",
                "hostname": "www.filmesonlinegratis.net",
                "lastSeen": "2015-09-26 05:52:23"
            },
            {
                "attributeType": "FacebookId",
                "attributeValue": "filmesog",
                "firstSeen": "2014-02-11 01:30:40",
                "hostname": "filmesonlinegratis.net",
                "lastSeen": "2015-09-24 05:12:39"
            },
            {
                "attributeType": "WhosAmungUsId",
                "attributeValue": "6cdg",
                "firstSeen": "2012-03-07 05:53:50",
                "hostname": "www.filmesonlinegratis.net",
                "lastSeen": "2012-03-07 16:00:45"
            }
        ]
    }
}

Human Readable Output

Total Retrieved Record(s): 10

TRACKERS

Hostname First (GMT) Last (GMT) Type Value
filmesonlinegratis.net 2016-10-14 10:16:38 2020-06-14 19:43:28 GoogleAnalyticsTrackingId ua-70630818-3
filmesonlinegratis.net 2016-10-14 10:16:38 2020-06-14 19:43:28 GoogleAnalyticsAccountNumber ua-70630818
www.filmesonlinegratis.net 2012-03-07 05:53:50 2016-10-13 15:38:35 GoogleAnalyticsAccountNumber ua-11598035
www.filmesonlinegratis.net 2012-03-07 05:53:50 2016-10-13 15:38:35 GoogleAnalyticsTrackingId ua-11598035-1
filmesonlinegratis.net 2014-02-11 01:30:40 2016-09-13 03:54:34 GoogleAnalyticsTrackingId ua-11598035-1
filmesonlinegratis.net 2014-02-11 01:30:40 2016-09-13 03:54:34 GoogleAnalyticsAccountNumber ua-11598035
www.filmesonlinegratis.net 2016-07-02 00:46:33 2016-09-02 11:09:30 TumblrId 25.media
www.filmesonlinegratis.net 2012-11-27 06:06:44 2015-09-26 05:52:23 FacebookId filmesog
filmesonlinegratis.net 2014-02-11 01:30:40 2015-09-24 05:12:39 FacebookId filmesog
www.filmesonlinegratis.net 2012-03-07 05:53:50 2012-03-07 16:00:45 WhosAmungUsId 6cdg

pt-get-pdns-details


Retrieves the passive DNS results from active account sources.

Base Command

pt-get-pdns-details

Input

Argument Name Description Required
query The domain or IP being queried. Required
start Filter for records whose last seen is after this datetime. It accepts “yyyy-mm-dd hh:mm:ss” or “yyyy-mm-dd” format. Optional
end Filter for records whose first seen is before this datetime. It accepts “yyyy-mm-dd hh:mm:ss” or “yyyy-mm-dd” format. Optional

Context Output

Path Type Description
PassiveTotal.PDNS.resolve String The host or ip address that indicates resolve in Passive DNS record.
PassiveTotal.PDNS.resolveType String The type of the resolve. I.e domain, ip, host, etc.
PassiveTotal.PDNS.value String The value of the Passive DNS record.
PassiveTotal.PDNS.source String Source of the passive DNS records.
PassiveTotal.PDNS.firstSeen String First seen timestamp of the passive DNS record.
PassiveTotal.PDNS.lastSeen String Last seen timestamp of the passive DNS record.
PassiveTotal.PDNS.collected String The date when a passive DNS record is collected.
PassiveTotal.PDNS.recordType String The type of the passive DNS record. I.e CNAME, SOA, A, etc
PassiveTotal.PDNS.recordHash String The hash value of the passive DNS record.
Domain.Name String The domain name, for example: ‘google.com’.
IP.Address String The IP Address of the component.
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.

Command Example

!pt-get-pdns-details query=www.furth.com.ar

Context Example

{
    "DBotScore": [
        {
            "Indicator": "furth.com.ar",
            "Score": 0,
            "Type": "domain",
            "Vendor": "PassiveTotal"
        },
        {
            "Indicator": "77.81.241.5",
            "Score": 0,
            "Type": "ip",
            "Vendor": "PassiveTotal"
        },
        {
            "Indicator": "184.75.255.33",
            "Score": 0,
            "Type": "ip",
            "Vendor": "PassiveTotal"
        }
    ],
    "Domain": {
        "Name": "furth.com.ar"
    },
    "IP": [
        {
            "Address": "77.81.241.5"
        },
        {
            "Address": "184.75.255.33"
        }
    ],
    "PassiveTotal": {
        "PDNS": [
            {
                "collected": "2020-06-17 12:26:33",
                "firstSeen": "2010-12-15 09:10:10",
                "lastSeen": "2020-06-17 05:26:33",
                "recordHash": "abf781b2484ea79d521cffb0745b71319d4db1158f71bb019b41077f8e55b035",
                "recordType": "CNAME",
                "resolve": "furth.com.ar",
                "resolveType": "domain",
                "source": [
                    "riskiq",
                    "pingly"
                ],
                "value": "www.furth.com.ar"
            },
            {
                "collected": "2020-06-17 12:26:33",
                "firstSeen": "2020-05-29 03:57:44",
                "lastSeen": "2020-06-17 05:26:33",
                "recordHash": "d7183564ca617e173fc26aeff66a38bb5c1b9089e56819851183860b9a37ccca",
                "recordType": "A",
                "resolve": "77.81.241.5",
                "resolveType": "ip",
                "source": [
                    "riskiq",
                    "pingly"
                ],
                "value": "www.furth.com.ar"
            },
            {
                "collected": "2020-06-17 12:26:33",
                "firstSeen": "2016-01-11 15:45:15",
                "lastSeen": "2017-10-24 08:53:52",
                "recordHash": "345780dcde96f0c28e3b93ec53bd33067f26075f30c2d4e49fafe0d2396194ca",
                "recordType": "A",
                "resolve": "184.75.255.33",
                "resolveType": "ip",
                "source": [
                    "riskiq"
                ],
                "value": "www.furth.com.ar"
            },
            {
                "collected": "2020-06-17 12:26:33",
                "firstSeen": "2020-06-17 05:26:33",
                "lastSeen": "2020-06-17 05:26:33",
                "recordHash": "63deb7c38cbea98f631777fd3ba89de0c270178bd37eb6a270ee7e37b3cd92e5",
                "recordType": "SOA",
                "resolve": "webmaster@furth.com.ar",
                "resolveType": "email",
                "source": [
                    "pingly"
                ],
                "value": "www.furth.com.ar"
            },
            {
                "collected": "2020-06-17 12:26:33",
                "firstSeen": "2020-06-17 05:26:33",
                "lastSeen": "2020-06-17 05:26:33",
                "recordHash": "24fa99da36eecc22b8970a33f8adf0f150598391319df4fc02128d677999e886",
                "recordType": "MX",
                "resolve": "furth.com.ar",
                "resolveType": "domain",
                "source": [
                    "pingly"
                ],
                "value": "www.furth.com.ar"
            }
        ]
    }
}

Human Readable Output

Total Retrieved Record(s): 5

PDNS detail(s)

Resolve Resolve Type Record Type Collected (GMT) First (GMT) Last (GMT) Source Record Hash
furth.com.ar domain CNAME 2020-06-17 12:26:33 2010-12-15 09:10:10 2020-06-17 05:26:33 riskiq, pingly abf781b2484ea79d521cffb0745b71319d4db1158f71bb019b41077f8e55b035
77.81.241.5 ip A 2020-06-17 12:26:33 2020-05-29 03:57:44 2020-06-17 05:26:33 riskiq, pingly d7183564ca617e173fc26aeff66a38bb5c1b9089e56819851183860b9a37ccca
184.75.255.33 ip A 2020-06-17 12:26:33 2016-01-11 15:45:15 2017-10-24 08:53:52 riskiq 345780dcde96f0c28e3b93ec53bd33067f26075f30c2d4e49fafe0d2396194ca
webmaster@furth.com.ar email SOA 2020-06-17 12:26:33 2020-06-17 05:26:33 2020-06-17 05:26:33 pingly 63deb7c38cbea98f631777fd3ba89de0c270178bd37eb6a270ee7e37b3cd92e5
furth.com.ar domain MX 2020-06-17 12:26:33 2020-06-17 05:26:33 2020-06-17 05:26:33 pingly 24fa99da36eecc22b8970a33f8adf0f150598391319df4fc02128d677999e886

pt-ssl-cert-search


Retrieves SSL certificates for a given field value.

Base Command

pt-ssl-cert-search

Input

Argument Name Description Required
field Field by which to search.

Allowed values: issuerSurname, subjectOrganizationName, issuerCountry, issuerOrganizationUnitName, fingerprint, subjectOrganizationUnitName, serialNumber, subjectEmailAddress, subjectCountry, issuerGivenName, subjectCommonName, issuerCommonName, issuerStateOrProvinceName, issuerProvince, subjectStateOrProvinceName, sha1, subjectStreetAddress, subjectSerialNumber, issuerOrganizationName, subjectSurname, subjectLocalityName, issuerStreetAddress, issuerLocalityName, subjectGivenName, subjectProvince, issuerSerialNumber, issuerEmailAddress
Required
query Field value for which to search. Required

Context Output

Path Type Description
PassiveTotal.SSL.firstSeen Number Epoch timestamp when SSL certificate identified by the system.
PassiveTotal.SSL.lastSeen Number The last seen epoch timestamp of the SSL certificates.
PassiveTotal.SSL.fingerprint String A fingerprint detail from the SSL certificates.
PassiveTotal.SSL.sslVersion Number A version of the certificate.
PassiveTotal.SSL.expirationDate String The expiry date of the certificate.
PassiveTotal.SSL.issueDate String Issue date of the certificate.
PassiveTotal.SSL.sha1 String Sha1 of the certificate.
PassiveTotal.SSL.serialNumber String A serial number of the certificate.
PassiveTotal.SSL.issuerCountry String The country name of the certificate issuer.
PassiveTotal.SSL.issuerStateOrProvinceName String The state or province name of the certificate issuer.
PassiveTotal.SSL.issuerCommonName String The common name of the issuer.
PassiveTotal.SSL.issuerEmailAddress String A contact email address of the certificate issuer.
PassiveTotal.SSL.issuerProvince String A province of the certificate issuer.
PassiveTotal.SSL.issuerOrganizationUnitName String An organization unit name of the certificate issuer.
PassiveTotal.SSL.issuerSurname String The surname of the certificate issuer.
PassiveTotal.SSL.issuerStreetAddress String Street address of the certificate issuer.
PassiveTotal.SSL.issuerLocalityName String The locality of the certificate issuer.
PassiveTotal.SSL.issuerSerialNumber String The serial number of the certificate issuer.
PassiveTotal.SSL.issuerOrganizationName String An organization name of the certificate issuer.
PassiveTotal.SSL.issuerGivenName String A given name of the certificate issuer.
PassiveTotal.SSL.subjectCommonName String The common name of the subject.
PassiveTotal.SSL.subjectOrganizationName String An organization name of the subject of the certificate.
PassiveTotal.SSL.subjectOrganizationUnitName String An organization unit name of the subject of the certificate.
PassiveTotal.SSL.subjectGivenName String The given name of the subject of the certificate.
PassiveTotal.SSL.subjectSurname String The surname of the subject of the certificate.
PassiveTotal.SSL.subjectLocalityName String The locality of the subject.
PassiveTotal.SSL.subjectEmailAddress String A contact email address of the subject.
PassiveTotal.SSL.subjectProvince String The province of the subject.
PassiveTotal.SSL.subjectStateOrProvinceName String The state or province name of the subject.
PassiveTotal.SSL.subjectSerialNumber String A serial number of the subject.
PassiveTotal.SSL.subjectStreetAddress String The street address of the subject.
PassiveTotal.SSL.subjectCountry String The country name of the subject from the certificate.
PassiveTotal.SSL.subjectAlternativeNames String Alternative names of the subject from the certificate details.

Command Example

!pt-ssl-cert-search field=serialNumber query=61135c80f8ed28d2

Context Example

{
    "PassiveTotal": {
        "SSL": [
            {
                "expirationDate": "Apr 09 13:15:00 2019 GMT",
                "fingerprint": "88:48:e8:68:b1:90:d0:fd:cb:6f:39:c3:7b:53:82:c8:7e:09:76:b0",
                "firstSeen": 1547559631314,
                "issueDate": "Jan 15 13:15:00 2019 GMT",
                "issuerCommonName": "Google Internet Authority G3",
                "issuerCountry": "US",
                "issuerOrganizationName": "Google Trust Services",
                "lastSeen": 1547607634446,
                "serialNumber": "6995036355238373586",
                "sha1": "8848e868b190d0fdcb6f39c37b5382c87e0976b0",
                "sslVersion": "3",
                "subjectAlternativeNames": [
                    "www.google.com"
                ],
                "subjectCommonName": "www.google.com",
                "subjectCountry": "US",
                "subjectLocalityName": "Mountain View",
                "subjectOrganizationName": "Google LLC",
                "subjectProvince": "California",
                "subjectStateOrProvinceName": "California"
            },
            {
                "expirationDate": "Apr 09 13:15:00 2019 GMT",
                "fingerprint": "99:5b:00:5f:44:be:53:bf:3e:59:21:90:1d:79:a9:8e:54:af:d3:29",
                "firstSeen": 1548455641692,
                "issueDate": "Jan 15 13:15:00 2019 GMT",
                "issuerCommonName": "Google Internet Authority G3",
                "issuerCountry": "US",
                "issuerOrganizationName": "Google Trust Services",
                "lastSeen": 1549571983939,
                "serialNumber": "6995036355238373586",
                "sha1": "995b005f44be53bf3e5921901d79a98e54afd329",
                "sslVersion": "3",
                "subjectAlternativeNames": [
                    "www.google.com"
                ],
                "subjectCommonName": "www.google.com",
                "subjectCountry": "US",
                "subjectLocalityName": "Mountain View",
                "subjectOrganizationName": "Google LLC",
                "subjectProvince": "California",
                "subjectStateOrProvinceName": "California"
            }
        ]
    }
}

Human Readable Output

Total Retrieved Record(s): 2

SSL certificate(s)

Sha1 Serial Number Issued (GMT) Expires (GMT) SSL Version First (GMT) Last (GMT) Issuer Common Name Subject Common Name Subject Alternative Names Issuer Organization Name Subject Organization Name Subject Locality Name Subject State/Province Name Issuer Country Subject Country
8848e868b190d0fdcb6f39c37b5382c87e0976b0 6995036355238373586 Jan 15 13:15:00 2019 GMT Apr 09 13:15:00 2019 GMT 3 2019-01-15 13:40:31 2019-01-16 03:00:34 Google Internet Authority G3 www.google.com www.google.com Google Trust Services Google LLC Mountain View California US US
995b005f44be53bf3e5921901d79a98e54afd329 6995036355238373586 Jan 15 13:15:00 2019 GMT Apr 09 13:15:00 2019 GMT 3 2019-01-25 22:34:01 2019-02-07 20:39:43 Google Internet Authority G3 www.google.com www.google.com Google Trust Services Google LLC Mountain View California US US

pt-get-host-pairs


Retrieves the host attribute pairs related to a domain or IP address. Maximum 2000 records are fetched.

Base Command

pt-get-host-pairs

Input

Argument Name Description Required
query Domain or IP address you want to search host-pairs for. Required
direction The direction of searching pair records for a given domain. Valid values: children, parents. Required
start Filter for records whose last seen is after this datetime. It accepts “yyyy-mm-dd hh:mm:ss” or “yyyy-mm-dd” format. Optional
end Filter for records whose first seen is before this datetime. It accepts “yyyy-mm-dd hh:mm:ss” or “yyyy-mm-dd” format. Optional

Context Output

Path Type Description
PassiveTotal.HostPair.firstSeen Date The date and time when the host pair was first observed.
PassiveTotal.HostPair.lastSeen Date The date and time when the host pair was most recently observed.
PassiveTotal.HostPair.cause String The cause of relation between parent and child.
PassiveTotal.HostPair.parent String The hostname of the parent of the host pair.
PassiveTotal.HostPair.child String The hostname of the child of the host pair.

Command Example

!pt-get-host-pairs direction=children query=ns1.furth.com.ar

Context Example

{
    "PassiveTotal": {
        "HostPair": [
            {
                "cause": "redirect",
                "child": "furth.com.ar",
                "firstSeen": "2020-05-29 07:05:22",
                "lastSeen": "2020-06-10 11:53:23",
                "parent": "ns1.furth.com.ar"
            },
            {
                "cause": "parentPage",
                "child": "ns1.furth.com.ar",
                "firstSeen": "2020-05-02 06:47:23",
                "lastSeen": "2020-06-08 03:08:38",
                "parent": "ns1.furth.com.ar"
            }
        ]
    }
}

Human Readable Output

Total Retrieved Record(s): 2

HOST PAIRS

Parent Hostname Child Hostname First (GMT) Last (GMT) Cause
ns1.furth.com.ar furth.com.ar 2020-05-29 07:05:22 2020-06-10 11:53:23 redirect
ns1.furth.com.ar ns1.furth.com.ar 2020-05-02 06:47:23 2020-06-08 03:08:38 parentPage

domain


Provides data enrichment for domains.

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

Base Command

domain

Input

Argument Name Description Required
domain The domain to enrich. Optional

Context Output

Path Type Description
Domain.Name String The domain name, for example: ‘google.com’.
Domain.WHOIS.CreationDate Date The date that the domain was created.
Domain.WHOIS.UpdatedDate Date The date that the domain was last updated.
Domain.WHOIS.ExpirationDate Date The expiration date of the domain.
Domain.WHOIS.NameServers String Name servers of the domain.
Domain.Organization String The organization of the domain.
Domain.Admin.Email String The email address of the domain administrator.
Domain.Admin.Name String The name of the domain administrator.
Domain.Admin.Phone String The phone number of the domain administrator.
Domain.Admin.Country String The country of the domain administrator.
Domain.Registrant.Email String The email address of the registrant.
Domain.Registrant.Name String The name of the registrant.
Domain.Registrant.Phone String The phone number for receiving abuse reports.
Domain.Registrant.Country String The country of the registrant.
Domain.WHOIS.Admin.Email String The email address of the domain administrator.
Domain.WHOIS.Admin.Name String The name of the domain administrator.
Domain.WHOIS.Admin.Phone String The phone number of the domain administrator.
Domain.WHOIS.Admin.Country String The country of the domain administrator.
Domain.WHOIS.Registrar.Name String The name of the registrar, for example: ‘GoDaddy’.
Domain.WHOIS.Registrant.Email String The email address of the registrant.
Domain.WHOIS.Registrant.Name String The name of the registrant.
Domain.WHOIS.Registrant.Phone String The phone number for receiving abuse reports.
Domain.WHOIS.Registrant.Country String The country of the registrant.
PassiveTotal.Domain.domain String The domain name, for example: ‘google.com’.
PassiveTotal.Domain.registrar String The name of the registrar of the domain
PassiveTotal.Domain.whoisServer String WHOIS server name where the details of domain registrations belong
PassiveTotal.Domain.registered Date The date that the domain was registered.
PassiveTotal.Domain.expiresAt Date The expiration date of the domain.
PassiveTotal.Domain.registryUpdatedAt Date The date when registry was last updated.
PassiveTotal.Domain.lastLoadedAt Date Last loaded date of WHOIS database.
PassiveTotal.Domain.nameServers String Name servers of the domain.
PassiveTotal.Domain.organization String The organization of the domain.
PassiveTotal.Domain.name String Name of the domain.
PassiveTotal.Domain.telephone String Telephone number fetched from whois details of the domain.
PassiveTotal.Domain.contactEmail String Contact Email address of the domain owner
PassiveTotal.Domain.registrantEmail String The name of the domain registrant.
PassiveTotal.Domain.registrantFax String The fax number of the domain registrant.
PassiveTotal.Domain.registrantName String The name of the domain registrant.
PassiveTotal.Domain.registrantOrganization String The organizations of the domain registrant.
PassiveTotal.Domain.registrantStreet String The street of the domain registrant.
PassiveTotal.Domain.registrantCity String The city of the domain registrant.
PassiveTotal.Domain.registrantState String The state of the domain registrant.
PassiveTotal.Domain.registrantPostalCode String The postal code of the domain registrant.
PassiveTotal.Domain.registrantCountry String The country of the domain registrant.
PassiveTotal.Domain.registrantTelephone String The telephone number of the domain registrant.
PassiveTotal.Domain.adminEmail String The email address of the domain administrator.
PassiveTotal.Domain.adminFax String The fax number of the domain administrator.
PassiveTotal.Domain.adminName String The name of the domain administrator.
PassiveTotal.Domain.adminOrganization String The organizations of the domain administrator.
PassiveTotal.Domain.adminStreet String The street of the domain administrator.
PassiveTotal.Domain.adminCity String The city of the domain administrator.
PassiveTotal.Domain.adminState String The state of the domain administrator.
PassiveTotal.Domain.adminPostalCode String The postal code of the domain administrator.
PassiveTotal.Domain.adminCountry String The country of the domain administrator.
PassiveTotal.Domain.adminTelephone String The telephone number of the domain administrator.
PassiveTotal.Domain.billingEmail String The email address of the domain billing.
PassiveTotal.Domain.billingFax String The fax number of the domain billing.
PassiveTotal.Domain.billingName String The name of the domain billing.
PassiveTotal.Domain.billingOrganization String The organizations of the domain billing.
PassiveTotal.Domain.billingStreet String The street of the domain billing.
PassiveTotal.Domain.billingCity String The city of the domain billing.
PassiveTotal.Domain.billingState String The state of the domain billing.
PassiveTotal.Domain.billingPostalCode String The postal code of the domain billing.
PassiveTotal.Domain.billingCountry String The country of the domain billing.
PassiveTotal.Domain.billingTelephone String The telephone number of the domain billing.
PassiveTotal.Domain.techEmail String The email address of the domain tech.
PassiveTotal.Domain.techFax String The fax number of the domain tech.
PassiveTotal.Domain.techName String The name of the domain tech.
PassiveTotal.Domain.techOrganization String The organizations of domain tech.
PassiveTotal.Domain.techStreet String The street of the domain tech.
PassiveTotal.Domain.techCity String The city of the domain tech.
PassiveTotal.Domain.techState String The state of the domain tech.
PassiveTotal.Domain.techPostalCode String The postal code of the domain tech.
PassiveTotal.Domain.techCountry String The country of the domain tech.
PassiveTotal.Domain.techTelephone String The telephone number of the domain tech.
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 DBot score.
PassiveTotal.Domain.score Number Reputation score of the indicator.
PassiveTotal.Domain.classification String Reputation classification of the indicator. (Can be GOOD, SUSPICIOUS, MALICIOUS, or UNKNOWN)
PassiveTotal.Domain.rules.name String Name of the rule that informed the reputation score of the indicator.
PassiveTotal.Domain.rules.description String Description of the rule.
PassiveTotal.Domain.rules.severity Number Severity of the rule.
PassiveTotal.Domain.rules.link String Link to the rule.

Command Example

!domain domain=riskiq.com

Context Example

{
    "DBotScore": [
        {
            "Indicator": "riskiq.com",
            "Score": 0,
            "Type": "domain",
            "Vendor": "PassiveTotal"
        }
    ],
    "Domain": [
        {
            "Admin": {
                "Country": "us",
                "Email": "domains@riskiq.com",
                "Name": "Risk IQ",
                "Phone": "18884154447"
            },
            "CreationDate": "2006-01-11T16:00:00.000-0800",
            "ExpirationDate": "2017-01-11T16:00:00.000-0800",
            "Name": "riskiq.com",
            "NameServers": [
                "luke.ns.cloudflare.com",
                "serena.ns.cloudflare.com"
            ],
            "Organization": "RiskIQ, Inc.",
            "Registrant": {
                "Country": "us",
                "Email": "domains@riskiq.com",
                "Name": "Risk IQ",
                "Phone": "18884154447"
            },
            "Registrar": {
                "AbuseEmail": null,
                "AbusePhone": null,
                "Name": "GODADDY.COM, LLC"
            },
            "UpdatedDate": "2014-12-08T16:00:00.000-0800",
            "WHOIS": {
                "Admin": {
                    "Country": "us",
                    "Email": "domains@riskiq.com",
                    "Name": "Risk IQ",
                    "Phone": "18884154447"
                },
                "CreationDate": "2006-01-11T16:00:00.000-0800",
                "ExpirationDate": "2017-01-11T16:00:00.000-0800",
                "NameServers": [
                    "luke.ns.cloudflare.com",
                    "serena.ns.cloudflare.com"
                ],
                "Registrant": {
                    "Country": "us",
                    "Email": "domains@riskiq.com",
                    "Name": "Risk IQ",
                    "Phone": "18884154447"
                },
                "Registrar": {
                    "AbuseEmail": null,
                    "AbusePhone": null,
                    "Name": "GODADDY.COM, LLC"
                },
                "UpdatedDate": "2014-12-08T16:00:00.000-0800"
            }
        }
    ],
    "PassiveTotal": {
        "Domain": {
            "adminCity": "san francisco",
            "adminCountry": "us",
            "adminEmail": "domains@riskiq.com",
            "adminName": "Risk IQ",
            "adminOrganization": "RiskIQ, Inc.",
            "adminPostalCode": "94111",
            "adminState": "california",
            "adminStreet": "22 Battery Street\n10th Floor",
            "adminTelephone": "18884154447",
            "contactEmail": "domains@riskiq.com",
            "domain": "riskiq.com",
            "expiresAt": "2017-01-11T16:00:00.000-0800",
            "lastLoadedAt": "2016-09-27T09:40:31.180-0700",
            "name": "Risk IQ",
            "nameServers": [
                "luke.ns.cloudflare.com",
                "serena.ns.cloudflare.com"
            ],
            "organization": "RiskIQ, Inc.",
            "registered": "2006-01-11T16:00:00.000-0800",
            "registrantCity": "san francisco",
            "registrantCountry": "us",
            "registrantEmail": "domains@riskiq.com",
            "registrantName": "Risk IQ",
            "registrantOrganization": "RiskIQ, Inc.",
            "registrantPostalCode": "94111",
            "registrantState": "california",
            "registrantStreet": "22 Battery Street\n10th Floor",
            "registrantTelephone": "18884154447",
            "registrar": "GODADDY.COM, LLC",
            "registryUpdatedAt": "2014-12-08T16:00:00.000-0800",
            "techCity": "san francisco",
            "techCountry": "us",
            "techEmail": "domains@riskiq.com",
            "techName": "Risk IQ",
            "techOrganization": "RiskIQ, Inc.",
            "techPostalCode": "94111",
            "techState": "california",
            "techStreet": "22 Battery Street\n10th Floor",
            "techTelephone": "18884154447",
            "telephone": "18884154447",
            "whoisServer": "whois.godaddy.com",
            "score": 3,
            "classification": "UNKNOWN",
              "rules": [
                {
                  "name": "Open ports observed",
                  "description": "The number of open ports may indicate maliciousness",
                  "severity": 3
                }
              ]
        }
    }
}

Human Readable Output

Domain(s)

|Domain|WHOIS Server|Registrar|Contact Email|Name Servers|Registrant|Admin|Tech|Creation Date (GMT)|Expire Date (GMT)|Updated Date (GMT)|Last Scanned (GMT)|
|—|—|—|—|—|—|—|—|—|—|—|—|
| riskiq.com | whois.godaddy.com | GODADDY.COM, LLC | domains@riskiq.com | luke.ns.cloudflare.com, serena.ns.cloudflare.com | City: san francisco,
Country: us,
Email: domains@riskiq.com,
Name: Risk IQ,
Organization: RiskIQ, Inc.,
PostalCode: 94111,
State: california,
Street: 22 Battery Street
10th Floor,
Telephone: 18884154447 | City: san francisco,
Country: us,
Email: domains@riskiq.com,
Name: Risk IQ,
Organization: RiskIQ, Inc.,
PostalCode: 94111,
State: california,
Street: 22 Battery Street
10th Floor,
Telephone: 18884154447 | City: san francisco,
Country: us,
Email: domains@riskiq.com,
Name: Risk IQ,
Organization: RiskIQ, Inc.,
PostalCode: 94111,
State: california,
Street: 22 Battery Street
10th Floor,
Telephone: 18884154447 | 2006-01-11T16:00:00.000-0800 | 2017-01-11T16:00:00.000-0800 | 2014-12-08T16:00:00.000-0800 | 2016-09-27T09:40:31.180-0700 |
The reputation score for ‘riskiq.com’ is 3 and is classified as ‘UNKNOWN’.

Reputation Rules

Name Description Severity
Open ports observed The number of open ports may indicate maliciousness 3

pt-get-services


Retrieves exposed services on the recently open ports for an IP address.

Base Command

pt-get-services

Input

Argument Name Description Required
ip IP address for which the user wants to search services for. Required

Context Output

Path Type Description
PassiveTotal.Service.ip String IP address of the service.
PassiveTotal.Service.portNumber Number Port number on which recent services were running or current services are running.
PassiveTotal.Service.firstSeen Date The date and time when the service was started for the first time on the port.
PassiveTotal.Service.lastSeen Date The date and time when the service was most recently used on the port.
PassiveTotal.Service.lastScan Date The date and time when the system performed the last scan to check whether any service is running on the port or not.
PassiveTotal.Service.count Number The total number of times service was used on the port.
PassiveTotal.Service.status String The status of the service.
PassiveTotal.Service.protocol String The protocol used by the service.
PassiveTotal.Service.banners.banner String The description of the banner generated as a result of scanning. Can be in HTML format.
PassiveTotal.Service.banners.scanType String The type of scan when the banner was generated.
PassiveTotal.Service.banners.firstSeen Date The date and time when the scan started.
PassiveTotal.Service.banners.lastSeen Date The date and time when the scan ended.
PassiveTotal.Service.banners.count Number The total number of times the same label was generated while scanning.
PassiveTotal.Service.currentServices.firstSeen Date The date and time when the current service started.
PassiveTotal.Service.currentServices.lastSeen Date The date and time when the current service was most recently used.
PassiveTotal.Service.currentServices.version String The version of the current service.
PassiveTotal.Service.currentServices.category String The category of the current service.
PassiveTotal.Service.currentServices.label String The label of the current service.
PassiveTotal.Service.recentServices.firstSeen Date The date and time when the recent service started.
PassiveTotal.Service.recentServices.lastSeen Date The date and time when the recent service was most recently used.
PassiveTotal.Service.recentServices.version String The version of the recent service.
PassiveTotal.Service.recentServices.category String The category of the recent service.
PassiveTotal.Service.recentServices.label String The label of the recent service.
PassiveTotal.Service.mostRecentSslCert.firstSeen Date The timestamp in epoch when the most recent SSL certificate was identified by the system.
PassiveTotal.Service.mostRecentSslCert.lastSeen Date The timestamp in epoch when the most recent SSL certificate was last used.
PassiveTotal.Service.mostRecentSslCert.fingerprint String A fingerprint detail from the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.sslVersion String The version of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.expirationDate Date The expiry date and time of the most recent SSL certificate in GMT.
PassiveTotal.Service.mostRecentSslCert.issueDate Date The date and time in GMT when the most recent SSL certificate was issued.
PassiveTotal.Service.mostRecentSslCert.sha1 String Sha1 of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.serialNumber String The serial Number of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectCountry String The name of the Country of the subject of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerCommonName String The common name of the issuer of most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerProvince String The province of the issuer of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectStateOrProvinceName String The state or province name of the subject of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectStreetAddress String The street address of the subject of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerStateOrProvinceName String The state or province name of the issuer of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectSurname String The surname of the subject of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerCountry String The country of the issuer of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectLocalityName String The subject locality name of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectAlternativeNames String List of alternative names of the subject of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerOrganizationUnitName String The name organization unit of the issuer of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerOrganizationName String The organization name of the issuer of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectEmailAddress String Email Address of the subject of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectOrganizationName String The organization name of the subject of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerLocalityName String The name of the locality of the issuer of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectCommonName String Common name of the subject of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectProvince String The province of the subject of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerGivenName String The given name of the issuer of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectOrganizationUnitName String Subject organization unit name of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerEmailAddress String The email address of the issuer of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectGivenName String Given name of the subject of the the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.subjectSerialNumber String The serial number of the subject of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerStreetAddress String The street Address of the issuer of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerSerialNumber String The serial number of the issuer of the most recent SSL certificate.
PassiveTotal.Service.mostRecentSslCert.issuerSurname String The surname of the issuer of the most recent SSL certificate.

Command Example

!pt-get-services ip=1.1.1.1

Context Example

{
  "PassiveTotal": {
    "Service": [
      {
        "count": 42335,
        "currentServices": [
          {
            "label": "Other Service"
          }
        ],
        "firstSeen": "2018-03-28 12:04:21",
        "ip": "1.1.1.1",
        "lastScan": "2021-03-06 02:28:14",
        "lastSeen": "2021-03-04 05:11:29",
        "portNumber": 53,
        "protocol": "UDP",
        "status": "filtered"
      },
      {
        "banners": [
          {
            "banner": "<html>\r\n<head><title>301 Moved Permanently</title></head>\r\n<body>\r\n<center><h1>301 Moved Permanently</h1></center>\r\n<hr><center>cloudflare</center>\r\n</body>\r\n</html>\r\n",
            "count": 3,
            "firstSeen": "2021-03-05 13:56:20",
            "lastSeen": "2021-03-06 00:20:32",
            "scanType": "http"
          }
        ],
        "count": 1386,
        "currentServices": [
          {
            "category": "Server",
            "firstSeen": "2019-06-18 13:45:42",
            "label": "CloudFlare",
            "lastSeen": "2021-03-10 07:58:44"
          },
          {
            "category": "Server",
            "firstSeen": "2020-07-09 16:19:47",
            "label": "cloudflare",
            "lastSeen": "2021-03-10 05:59:33"
          },
          {
            "category": "Server",
            "firstSeen": "2018-07-02 11:46:37",
            "label": "yunjiasu-nginx",
            "lastSeen": "2021-03-09 02:26:20"
          }
        ],
        "firstSeen": "2018-04-01 00:38:56",
        "ip": "1.1.1.1",
        "lastScan": "2021-03-06 13:27:15",
        "lastSeen": "2021-03-06 00:20:32",
        "portNumber": 80,
        "protocol": "TCP",
        "recentServices": [
          {
            "category": "Server",
            "firstSeen": "2020-03-18 20:37:06",
            "label": "BigIP",
            "lastSeen": "2021-03-05 21:31:27"
          },
          {
            "category": "Server",
            "firstSeen": "2020-06-11 11:50:49",
            "label": "F5 BIG-IP load balancer httpd",
            "lastSeen": "2021-03-05 21:31:27"
          },
          {
            "category": "Server",
            "firstSeen": "2020-10-27 12:39:22",
            "label": "OpenResty web app server",
            "lastSeen": "2021-02-27 19:59:14"
          },
          {
            "category": "Server",
            "firstSeen": "2019-02-09 11:59:43",
            "label": "openresty",
            "lastSeen": "2021-02-27 19:59:14"
          },
          {
            "category": "Server",
            "firstSeen": "2018-08-05 00:56:16",
            "label": "Apache",
            "lastSeen": "2020-11-09 07:02:20"
          }
        ],
        "status": "open"
      },
      {
        "count": 41,
        "currentServices": [
          {
            "label": "Other Service"
          }
        ],
        "firstSeen": "2020-02-29 04:02:09",
        "ip": "1.1.1.1",
        "lastScan": "2021-03-06 06:51:11",
        "lastSeen": "2021-02-27 16:00:28",
        "portNumber": 111,
        "protocol": "UDP",
        "status": "closed"
      }
    ]
  }
}

Human Readable Output

Total Retrieved Record(s) 13

Services

Port Number Protocol Status Current Service Labels First Seen Date (GMT) Last Seen Date (GMT) Last Scanned Date (GMT)
53 UDP filtered Other Service 2018-03-28 12:04:21 2021-03-04 05:11:29 2021-03-06 02:28:14
80 TCP open CloudFlare, cloudflare, yunjiasu-nginx 2018-04-01 00:38:56 2021-03-06 00:20:32 2021-03-06 13:27:15
111 UDP closed Other Service 2020-02-29 04:02:09 2021-02-27 16:00:28 2021-03-06 06:51:11

pt-get-whois


Gets WHOIS information records based on queries.

Base Command

pt-get-whois

Input

Argument Name Description Required
query Query value to use in the request. For example: riskiq.com, 1.1.1.1 Required
history Whether to return historical results. Valid values: true, false. Optional

Context Output

Path Type Description
PassiveTotal.WHOIS.domain String The domain name. For example: ‘google.com’.
PassiveTotal.WHOIS.registrar String The name of the registrar of the domain.
PassiveTotal.WHOIS.whoisServer String WHOIS server name where the details of domain registrations belong.
PassiveTotal.WHOIS.registered Date The date that the domain was registered.
PassiveTotal.WHOIS.expiresAt Date The expiration date of the domain.
PassiveTotal.WHOIS.registryUpdatedAt Date The date when the registry was last updated.
PassiveTotal.WHOIS.lastLoadedAt Date Last loaded date of WHOIS database.
PassiveTotal.WHOIS.nameServers String Name servers of the domain.
PassiveTotal.WHOIS.organization String The organization of the domain.
PassiveTotal.WHOIS.name String Name of the domain.
PassiveTotal.WHOIS.telephone String Telephone number fetched from whois details of the domain.
PassiveTotal.WHOIS.contactEmail String Contact Email address of the domain owner.
PassiveTotal.WHOIS.registrantEmail String The name of the domain registrant.
PassiveTotal.WHOIS.registrantFax String The fax number of the domain registrant.
PassiveTotal.WHOIS.registrantName String The name of the domain registrant.
PassiveTotal.WHOIS.registrantOrganization String The organizations of the domain registrant.
PassiveTotal.WHOIS.registrantStreet String The street of the domain registrant.
PassiveTotal.WHOIS.registrantCity String The city of the domain registrant.
PassiveTotal.WHOIS.registrantState String The state of the domain registrant.
PassiveTotal.WHOIS.registrantPostalCode String The postal code of the domain registrant.
PassiveTotal.WHOIS.registrantCountry String The country of the domain registrant.
PassiveTotal.WHOIS.registrantTelephone String The telephone number of the domain registrant.
PassiveTotal.WHOIS.adminEmail String The email address of the domain administrator.
PassiveTotal.WHOIS.adminFax String The fax number of the domain administrator.
PassiveTotal.WHOIS.adminName String The name of the domain administrator.
PassiveTotal.WHOIS.adminOrganization String The organizations of the domain administrator.
PassiveTotal.WHOIS.adminStreet String The street of the domain administrator.
PassiveTotal.WHOIS.adminCity String The city of the domain administrator.
PassiveTotal.WHOIS.adminState String The state of the domain administrator.
PassiveTotal.WHOIS.adminPostalCode String The postal code of the domain administrator.
PassiveTotal.WHOIS.adminCountry String The country of the domain administrator.
PassiveTotal.WHOIS.adminTelephone String The telephone number of the domain administrator.
PassiveTotal.WHOIS.billingEmail String The email address of the domain billing.
PassiveTotal.WHOIS.billingFax String The fax number of the domain billing.
PassiveTotal.WHOIS.billingName String The name of the domain billing.
PassiveTotal.WHOIS.billingOrganization String The organizations of the domain billing.
PassiveTotal.WHOIS.billingStreet String The street of the domain billing.
PassiveTotal.WHOIS.billingCity String The city of the domain billing.
PassiveTotal.WHOIS.billingState String The state of the domain billing.
PassiveTotal.WHOIS.billingPostalCode String The postal code of the domain billing.
PassiveTotal.WHOIS.billingCountry String The country of the domain billing.
PassiveTotal.WHOIS.billingTelephone String The telephone number of the domain billing.
PassiveTotal.WHOIS.techEmail String The email address of the domain tech.
PassiveTotal.WHOIS.techFax String The fax number of the domain tech.
PassiveTotal.WHOIS.techName String The name of the domain tech.
PassiveTotal.WHOIS.techOrganization String The organizations of domain tech.
PassiveTotal.WHOIS.techStreet String The street of the domain tech.
PassiveTotal.WHOIS.techCity String The city of the domain tech.
PassiveTotal.WHOIS.techState String The state of the domain tech.
PassiveTotal.WHOIS.techPostalCode String The postal code of the domain tech.
PassiveTotal.WHOIS.techCountry String The country of the domain tech.
PassiveTotal.WHOIS.techTelephone String The telephone number of the domain tech.

Command Example

!pt-get-whois query=riskiq.com

Context Example

{
    "PassiveTotal": {
        "WHOIS": {
            "adminCity": "san francisco",
            "adminCountry": "us",
            "adminEmail": "domains@riskiq.com",
            "adminName": "Risk IQ",
            "adminOrganization": "RiskIQ, Inc.",
            "adminPostalCode": "94111",
            "adminState": "california",
            "adminStreet": "22 Battery Street\n10th Floor",
            "adminTelephone": "18884154447",
            "contactEmail": "domains@riskiq.com",
            "domain": "riskiq.com",
            "expiresAt": "2017-01-11T16:00:00.000-0800",
            "lastLoadedAt": "2016-09-27T09:40:31.180-0700",
            "name": "Risk IQ",
            "nameServers": [
                "luke.ns.cloudflare.com",
                "serena.ns.cloudflare.com"
            ],
            "organization": "RiskIQ, Inc.",
            "registered": "2006-01-11T16:00:00.000-0800",
            "registrantCity": "san francisco",
            "registrantCountry": "us",
            "registrantEmail": "domains@riskiq.com",
            "registrantName": "Risk IQ",
            "registrantOrganization": "RiskIQ, Inc.",
            "registrantPostalCode": "94111",
            "registrantState": "california",
            "registrantStreet": "22 Battery Street\n10th Floor",
            "registrantTelephone": "18884154447",
            "registrar": "GODADDY.COM, LLC",
            "registryUpdatedAt": "2014-12-08T16:00:00.000-0800",
            "techCity": "san francisco",
            "techCountry": "us",
            "techEmail": "domains@riskiq.com",
            "techName": "Risk IQ",
            "techOrganization": "RiskIQ, Inc.",
            "techPostalCode": "94111",
            "techState": "california",
            "techStreet": "22 Battery Street\n10th Floor",
            "techTelephone": "18884154447",
            "telephone": "18884154447",
            "whoisServer": "whois.godaddy.com"
        }
    }
}

Human Readable Output

Total Retrieved Record(s): 1

Associated Domains

Domain WHOIS Server Registrar Contact Email Name Servers Registrant Admin Tech Creation Date (GMT) Expire Date (GMT) Updated Date (GMT) Last Scanned (GMT)
riskiq.com whois.godaddy.com GODADDY.COM, LLC domains@riskiq.com luke.ns.cloudflare.com, serena.ns.cloudflare.com City: san francisco,
Country: us,
Email: domains@riskiq.com,
Name: Risk IQ,
Organization: RiskIQ, Inc.,
PostalCode: 94111,
State: california,
Street: 22 Battery Street
10th Floor,
Telephone: 18884154447
City: san francisco,
Country: us,
Email: domains@riskiq.com,
Name: Risk IQ,
Organization: RiskIQ, Inc.,
PostalCode: 94111,
State: california,
Street: 22 Battery Street
10th Floor,
Telephone: 18884154447
City: san francisco,
Country: us,
Email: domains@riskiq.com,
Name: Risk IQ,
Organization: RiskIQ, Inc.,
PostalCode: 94111,
State: california,
Street: 22 Battery Street
10th Floor,
Telephone: 18884154447
2006-01-11T16:00:00.000-0800 2017-01-11T16:00:00.000-0800 2014-12-08T16:00:00.000-0800 2016-09-27T09:40:31.180-0700

pt-get-cookies


Retrieves cookies addresses or hostname information based on cookie name or domain.

Base Command

pt-get-cookies

Input

Argument Name Description Required
search_by Search cookies information by name or domain. Valid values: 1. get addresses by cookie domain, 2. get addresses by cookie name, 3. get hosts by cookie domain, 4. get hosts by cookie name. Required
query Name or domain of cookie the user wants to search for. Required
page Page number for paging through results. Each page contains 2000 values. Default is 0. Optional
sort Field to sort the results on. Valid values: last seen, first seen. Default is last seen. Optional
order Order to return the results in. Valid values: asc, desc. Default is desc. Optional

Context Output

Path Type Description
PassiveTotal.Cookie.hostname String The hostname/IP of the machine on which the cookie was found.
PassiveTotal.Cookie.cookieName String The name of the cookie that was found on the host.
PassiveTotal.Cookie.cookieDomain String The domain from which the cookie originated from.
PassiveTotal.Cookie.firstSeen Date The date and time when the cookie was first observed.
PassiveTotal.Cookie.lastSeen Date The date and time when the cookie was most recently observed.

Command Example

!pt-get-cookies search_by="get hosts by cookie name" query=dummyCookie

Context Example

{
    "PassiveTotal": {
        "Cookie": [
            {
                "cookieDomain": "dummy.domain",
                "cookieName": "dummyCookie",
                "firstSeen": "2016-11-22 03:36:07",
                "hostname": "dummy.domain",
                "lastSeen": "2017-07-27 21:05:10"
            }
        ]
    }
}

Human Readable Output

Total Record(s): 1

Total Retrieved Record(s): 1

Cookies

Hostname Cookie Name Cookie Domain First Seen Date (GMT) Last Seen Date (GMT)
dummy.domain dummyCookie dummy.domain 2019-04-02 01:53:50 2021-01-22 07:15:13

pt-get-articles


Retrieves information related to articles for a specific indicator.

Base Command

pt-get-articles

Input

Argument Name Description Required
query Indicator value to search for in articles. For example: riskiq.com, 1.1.1.1 Required
type Type of the indicator. For example: domain, ip, url Optional

Context Output

Path Type Description
PassiveTotal.Article.guid String The global unique ID of the article.
PassiveTotal.Article.title String The title of the article.
PassiveTotal.Article.summary String The summary of the article.
PassiveTotal.Article.type String The type of an article.
PassiveTotal.Article.publishedDate Date The date and time on which the article was published.
PassiveTotal.Article.link String The link of the article for getting more details.
PassiveTotal.Article.categories Unknown An array of categories of the article.
PassiveTotal.Article.tags Unknown An array of tags for the article.
PassiveTotal.Article.indicators.type String The type of the indicator.
PassiveTotal.Article.indicators.count Number Total number of indicators of a particular type.
PassiveTotal.Article.indicators.values Unknown An array of values related to indicators.
PassiveTotal.Article.indicators.source String The source of the indicator.

Command Example

!pt-get-articles query=dummy.com

Context Example

{
    "PassiveTotal": {
        "Article": {
            "categories": [
                "Categories 1",
                "Categories 2"
            ],
            "guid": "12e123b1",
            "indicators": [
                {
                    "count": 1,
                    "source": "public",
                    "type": "domain",
                    "values": [
                        "dummy.com"
                    ]
                }
            ],
            "link": "https://community.riskiq.com/article/12e123b1",
            "publishedDate": "Mon Aug 29 20:00:00 VET 2016",
            "summary": "Did you know that you can get all kinds of free stuff, just by giving out your personal information? The internet is full of these fake reward scams which RiskIQ's sytems surface every hour of the day.",
            "tags": [
                "fake rewards",
                "playstation",
                "scam"
            ],
            "title": "Free PlayStations on the Internet are Probably an Online Scam",
            "type": "public"
        }
    }
}

Human Readable Output

Total Retrieved Record(s): 1

Article(s)

GUID Title Summary Type Tags Categories Article Link Published Date (GMT)
12e123b1 Free PlayStations on the Internet are Probably an Online Scam Did you know that you can get all kinds of free stuff, just by giving out your personal information? The internet is full of these fake reward scams which RiskIQ’s sytems surface every hour of the day. public fake rewards, playstation, scam Categories 1, Categories 2 https://community.riskiq.com/article/12e123b1 Mon Aug 29 20:00:00 VET 2016

pt-get-data-card


Retrieves a summary data card associated with the given query.

Base Command

pt-get-data-card

Input

Argument Name Description Required
query The domain, host or IP address to be queried. For example: riskiq.com, 1.1.1.1. Required

Context Output

Path Type Description
PassiveTotal.DataCard.type String Type of the indicator.
PassiveTotal.DataCard.name String Name of the indicator.
PassiveTotal.DataCard.link String Link to the indicator.
PassiveTotal.DataCard.netblock String Netblock associated with the indicator.
PassiveTotal.DataCard.os String Operating system associated with the indicator.
PassiveTotal.DataCard.organization String The organization of the indicator.
PassiveTotal.DataCard.asn String Autonomous system number assigned to the indicator.
PassiveTotal.DataCard.hosting_provider String Host provider of the indicator.
PassiveTotal.DataCard.data_summary.resolutions.count Number Number of resolutions attached to the indicator.
PassiveTotal.DataCard.data_summary.resolutions.link String Link of the resolutions attached to the indicator.
PassiveTotal.DataCard.data_summary.services.count Number Number of service records for the indicator.
PassiveTotal.DataCard.data_summary.services.link String Link to the service records of the indicator.
PassiveTotal.DataCard.data_summary.certificates.count Number Number of certificates for the given indicator.
PassiveTotal.DataCard.data_summary.certificates.link String Link to the certificates associated with the indicator.
PassiveTotal.DataCard.data_summary.hashes.count Number Number of hashes associated with the indicator.
PassiveTotal.DataCard.data_summary.hashes.link String Link to the hashes associated with the indicator.
PassiveTotal.DataCard.data_summary.projects.count Number Number of projects containing the indicator.
PassiveTotal.DataCard.data_summary.projects.link String Number of projects containing the indicator.
PassiveTotal.DataCard.data_summary.articles.count Number Number of articles referencing the indicator.
PassiveTotal.DataCard.data_summary.articles.link String Link to the articles referencing the indicator.
PassiveTotal.DataCard.data_summary.trackers.count Number Number of trackers associated with the indicator.
PassiveTotal.DataCard.data_summary.trackers.link String Link to the trackers associated with the indicator.
PassiveTotal.DataCard.data_summary.components.count Number Number of components associated with the indicator.
PassiveTotal.DataCard.data_summary.components.link String Link to the components associated with the indicator.
PassiveTotal.DataCard.data_summary.host_pairs.count Number Number of host pairs associated with the indicator.
PassiveTotal.DataCard.data_summary.host_pairs.link String Link to the host pairs associated with the indicator.
PassiveTotal.DataCard.data_summary.reverse_dns.count Number Number of DNS records for the indicator.
PassiveTotal.DataCard.data_summary.reverse_dns.link String Link to the DNS records of the indicator.
PassiveTotal.DataCard.data_summary.cookies.count Number Number of available cookie records for the indicator.
PassiveTotal.DataCard.data_summary.cookies.link String Link to the cookie records for the indicator.

Command Example

!pt-get-data-card query="1.1.1.1"

Context Example

{
    "PassiveTotal": {
        "DataCard": {
            "asn": "AS13335 - CLOUDFLARENET",
            "data_summary": {
                "articles": {
                    "count": 0,
                    "link": "https://community.pt.com/research/1.1.1.1"
                },
                "certificates": {
                    "count": 3742,
                    "link": "https://community.pt.com/search/1.1.1.1/domaincertificates"
                },
                "components": {
                    "count": 914,
                    "link": "https://community.pt.com/search/1.1.1.1/components"
                },
                "cookies": {
                    "count": 23346,
                    "link": "https://community.pt.com/search/1.1.1.1/cookies"
                },
                "hashes": {
                    "count": 1000,
                    "link": "https://community.pt.com/search/1.1.1.1/hashes"
                },
                "host_pairs": {
                    "count": 6987,
                    "link": "https://community.pt.com/search/1.1.1.1/hostpairs"
                },
                "projects": {
                    "count": 4,
                    "link": "https://community.pt.com/search/1.1.1.1/projects"
                },
                "resolutions": {
                    "count": 1997,
                    "link": "https://community.pt.com/search/1.1.1.1/resolutions"
                },
                "reverse_dns": {
                    "count": 5,
                    "link": "https://community.pt.com/search/1.1.1.1/dns"
                },
                "services": {
                    "count": 3,
                    "link": "https://community.pt.com/search/1.1.1.1/services"
                },
                "trackers": {
                    "count": 3983,
                    "link": "https://community.pt.com/search/1.1.1.1/trackers"
                }
            },
            "hosting_provider": "Cloudflare",
            "link": "https://community.pt.com/search/1.1.1.1",
            "name": "1.1.1.1",
            "netblock": "1.1.1.0/24",
            "organization": "Cloudflare, Inc.",
            "os": "CentOS",
            "type": "IP Address"
        }
    }
}

Human Readable Output

Data Card Summary

Name Type Netblock Autonomous System Number Host Provider Operating System Data Card Summary
1.1.1.1 IP Address 1.1.1.0/24 AS13335 - CLOUDFLARENET Cloudflare CentOS Resolutions: 1997, Services: 3, Certificates: 3742, Hashes: 1000, Projects: 4, Articles: 0, Trackers: 3983, Components: 914, Host Pairs: 6987, Reverse Dns: 5, Cookies: 23346

pt-get-reputation


Gets reputation for a given domain, host or IP.

Base Command

pt-get-reputation

Input

Argument Name Description Required
query The domain, host or IP address to be queried. For example: riskiq.com, 1.1.1.1. Required

Context Output

Path Type Description
PassiveTotal.Reputation.query String The value of the indicator.
PassiveTotal.Reputation.score Number Reputation score of the indicator.
PassiveTotal.Reputation.classification String Reputation classification of the indicator. (Can be GOOD, SUSPICIOUS, MALICIOUS, or UNKNOWN)
PassiveTotal.Reputation.rules.name String Name of the rule that informed the reputation score of the indicator.
PassiveTotal.Reputation.rules.description String Description of the rule.
PassiveTotal.Reputation.rules.severity Number Severity of the rule.
PassiveTotal.Reputation.rules.link String Link to the rule.

Command Example

!pt-get-reputation query="amazon.hksupd.com"

Context Example

{
    "PassiveTotal": {
        "Reputation": {
            "classification": "MALICIOUS",
            "query": "amazon.hksupd.com",
            "rules": [
                {
                    "description": "Vermilion Strike: Linux and Windows Re-implementation of Cobalt Strike",
                    "link": "https://community.pt.com/article/d0bf5d18",
                    "name": "RiskIQ Intel Article",
                    "severity": 5
                }
            ],
            "score": 100
        }
    }
}

Human Readable Output

The reputation score for ‘amazon.hksupd.com’ is 100 and is classified as ‘MALICIOUS’.

Reputation Rules

Name Description Severity
RiskIQ Intel Article Vermilion Strike: Linux and Windows Re-implementation of Cobalt Strike 5

ip


Checks the reputation of an IP address.

Base Command

ip

Input

Argument Name Description Required
ip The IP address to check. Optional

Context Output

Path Type Description
PassiveTotal.IP.query String The value of the indicator.
PassiveTotal.IP.score Number Reputation score of the indicator.
PassiveTotal.IP.classification String Reputation classification of the indicator. (Can be GOOD, SUSPICIOUS, MALICIOUS, or UNKNOWN)
PassiveTotal.IP.rules.name String Name of the rule that informed the reputation score of the indicator.
PassiveTotal.IP.rules.description String Description of the rule.
PassiveTotal.IP.rules.severity Number Severity of the rule.
PassiveTotal.IP.rules.link String Link to the rule.
DBotScore.Indicator String The indicator that was tested.
DBotScore.Score Number The actual score.
DBotScore.Type String The indicator type.
DBotScore.Vendor String The vendor used to calculate the score.
IP.Address String The IP Address.

Command Example

!ip ip=8.8.8.8

Context Example

{
    "DBotScore": {
        "Indicator": "8.8.8.8",
        "Score": 0,
        "Type": "ip",
        "Vendor": "PassiveTotal v2"
    },
    "IP": {
        "Address": "8.8.8.8"
    },
    "PassiveTotal": {
        "IP": {
            "classification": "UNKNOWN",
            "query": "8.8.8.8",
            "rules": [
                {
                    "description": "The number of open ports may indicate maliciousness",
                    "name": "Open ports observed",
                    "severity": 3
                }
            ],
            "score": 3
        }
    }
}

Human Readable Output

The reputation score for ‘8.8.8.8’ is 3 and is classified as ‘UNKNOWN’.

Reputation Rules

Name Description Severity
Open ports observed The number of open ports may indicate maliciousness 3

pt-list-intel-profiles


Retrieves the list of all profiles.

Base Command

pt-list-intel-profiles

Input

Argument Name Description Required
id Specify the ID of the profile to retrieve the specific profile.

Note: If ‘id’ argument is provided, all other arguments will be neglected.
Optional
query Filter the result based on title or aliases. . Optional
type Filter the results based on the profile type.

Possible values: actor, tool, backdoor.
Optional
indicator_value Specify the indicator value to retrieve the profiles containing the given indicator.

Note: To retrieve the list of indicators, execute the “pt-list-intel-profile-indicators” command.
When both indicator_value and query are provided, higher priority will be given to indicator_value.
Optional
source Filter the result based on the indicator source.

Possible values: osint, riskiq.

Note: Requires ‘indicator_value’ argument.
Optional
category Filter the result based on the indicator category.

Possible values: host, network.

Note: Requires ‘indicator_value’ argument.
Optional
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is 1000. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.IntelProfile.id String ID of the intel profile.
PassiveTotal.IntelProfile.title String Title of the intel profile.
PassiveTotal.IntelProfile.link String Link to the intel profile.
PassiveTotal.IntelProfile.osintIndicatorsCount Number Count of the open source intelligence indicators referencing the intel profile.
PassiveTotal.IntelProfile.riskIqIndicatorsCount Number Count of the riskiq indicators referencing the intel profile.
PassiveTotal.IntelProfile.indicators String Link to the indicators referencing the intel profile.
PassiveTotal.IntelProfile.aliases String Aliases of the intel profile.
PassiveTotal.IntelProfile.tags.label String Labels associated with the intel profile.
PassiveTotal.IntelProfile.tags.countryCode String Country code of the tags associated with the intel profile.

Command Example

!pt-list-intel-profiles id="apt33"

Context Example

{
    "PassiveTotal": {
        "IntelProfile": {
            "aliases": [
                "Elfin",
                "Magnallium",
                "Refined Kitten",
                "Holmium",
                "Cobalt Trinity"
            ],
            "id": "apt33",
            "indicators": "https://api.pt.net/pt/v2/intel-profiles/apt33/indicators",
            "link": "https://community.pt.com/intel-profiles/apt33",
            "osintIndicatorsCount": 410,
            "riskIqIndicatorsCount": 122,
            "tags": [
                {
                    "label": "Espionage"
                },
                {
                    "label": "Sabotage"
                },
                {
                    "label": "Windows"
                },
                {
                    "label": "Aviation"
                },
                {
                    "label": "Defense"
                },
                {
                    "label": "Oil & Gas"
                },
                {
                    "countryCode": "ir",
                    "label": "State Sponsored: Iran"
                },
                {
                    "countryCode": "sa",
                    "label": "Target: Saudi Arabia"
                },
                {
                    "countryCode": "us",
                    "label": "Target: USA"
                },
                {
                    "countryCode": "kr",
                    "label": "Target: South Korea"
                },
                {
                    "countryCode": "il",
                    "label": "Target: Israel"
                }
            ],
            "title": "APT33"
        }
    }
}

Human Readable Output

Profile(s)

ID Title Aliases Public Indicators RiskIQ Indicators
apt33 APT33 Elfin, Magnallium, Refined Kitten, Holmium, Cobalt Trinity 410 122

pt-list-intel-profile-indicators


Retrieves the indicators for the given profile.

Base Command

pt-list-intel-profile-indicators

Input

Argument Name Description Required
id Specify the ID of the profile to retrieve indicators for the specific profile.

Note: To retrieve the list of profile IDs, execute the “pt-list-intel-profile” command.
Required
type Filter the results based on the indicator type.

Possible values: certificate_sha1, domain, email, hash_md5, hash_sha256, ip, pdb_path, soa_email, url, whois_email.
Optional
indicator_value Specify the indicator value to retrieve the specific indicator. Optional
source Filter the result based on the indicator source.

Possible values: osint, riskiq.
Optional
category Filter the result based on the indicator category.

Possible values: host, network.
Optional
page_number Page number for paging through results.

Note: The minimum value supported is 0 and maximum value supported is int32. Default is 0.
Optional
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is int32. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.IntelProfile.id String Profile ID containing the indicator.
PassiveTotal.IntelProfile.indicator.id String ID of the indicator.
PassiveTotal.IntelProfile.indicator.type String Type of the indicator.
PassiveTotal.IntelProfile.indicator.value String Value of the indicator.
PassiveTotal.IntelProfile.indicator.category String Category of the indicator.
PassiveTotal.IntelProfile.indicator.firstSeen String Date & time the record was first seen.
PassiveTotal.IntelProfile.indicator.lastSeen String Date & time the record was most recently observed.
PassiveTotal.IntelProfile.indicator.osint String Whether the indicator was published in open source intelligence articles.
PassiveTotal.IntelProfile.indicator.osintUrl String Link to the osint source of the indicator.
PassiveTotal.IntelProfile.indicator.articleGuids String List of RiskIQ OSINT article GUIDs associated with the indicator.

Command Example

!pt-list-intel-profile-indicators id="apt33" page_size=1

Context Example

{
    "PassiveTotal": {
        "IntelProfile": {
            "id": "apt33",
            "indicator": [
                {
                    "articleGuids": [
                        "633605c6"
                    ],
                    "category": "host",
                    "firstSeen": "2017-03-06T00:00:00.000+00:00",
                    "id": "apt33:00c417425a73db5a315d23fac8cb353f",
                    "osint": true,
                    "type": "hash_md5",
                    "value": "00c417425a73db5a315d23fac8cb353f",
                    "lastSeen": "2017-03-06T00:00:00.000+00:00"
                }
            ]
        }
    }
}

Human Readable Output

Total Retrieved Indicator(s) 532

Indicator(s)

ID Artifact Value Type First Seen (GMT) Last Seen (GMT) Source
apt33:00c417425a73db5a315d23fac8cb353f 00c417425a73db5a315d23fac8cb353f hash_md5 2017-03-06T00:00:00.000+00:00 2017-03-06T00:00:00.000+00:00 OSINT

pt-list-my-attack-surface-insights


Retrieves the attack surface insight information of the individual’s account.

Base Command

pt-list-my-attack-surface-insights

Input

Argument Name Description Required
priority Filter the results based on the priority level specified.

Possible values: high, medium, low.
Required
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is 1000. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.Summary.Insight.name String The command name.
PassiveTotal.Summary.Insight.activeInsightCount Number Total number of active insights.
PassiveTotal.Summary.Insight.totalInsightCount Number Total number of insights.
PassiveTotal.Summary.Insight.totalObservations Number Total number of observations.
PassiveTotal.Insight.priorityLevel String Priority level of insights.
PassiveTotal.Insight.insight.name String Name of the insight.
PassiveTotal.Insight.insight.description String Description of the insight.
PassiveTotal.Insight.insight.observationCount Number Number of observations for the given insight.
PassiveTotal.Insight.insight.link String Link to the insight.
PassiveTotal.Insight.insight.insightId String ID of the third party insight.
PassiveTotal.Insight.insight.segmentBy String Segment by of the insight.

Command Example

!pt-list-my-attack-surface-insights priority="low"

Context Example

{
    "PassiveTotal": {
        "Insight": {
            "insight": [
                {
                    "description": "##### Description \nThe following SSL certificates are SHA-1 certificates and are no longer recognized by web browsers due to possible hash collision.\n\n##### Remediation\nOrganizations should replace these certificates with new SSL certificate that use SHA-256.",
                    "insightId": "40466",
                    "link": "https://api.pt.net/pt/v2/attack-surface/insight/40466?page=0&size=25&groupBy=RISK_CATEGORY&segmentBy=savedfilter_metric_29630",
                    "name": "ASI: SHA-1 Certificates",
                    "observationCount": 0,
                    "segmentBy": "savedfilter_metric_29630"
                },
                {
                    "description": "##### Description \nDeprecated versions of Nginx web server that are no longer supported.  Running end of life or deprecated hardware or software can open organizations up to potential risks and vulnerabilities as these systems are no longer supported via regular updates and security patches\n\n##### Remediation\nOrganizations should consider upgrading to supported versions of Nginx to ensure security patches are available.\n",
                    "insightId": "40466",
                    "link": "https://api.pt.net/pt/v2/attack-surface/insight/40466?page=0&size=25&groupBy=RISK_CATEGORY&segmentBy=savedfilter_metric_29643",
                    "name": "ASI:  Deprecated Tech - Nginx",
                    "observationCount": 146,
                    "segmentBy": "savedfilter_metric_29643"
                }
            ],
            "priorityLevel": "low"
        },
        "Summary": {
            "Insight": {
                "activeInsightCount": 6,
                "name": "pt-list-my-attack-surface-insights",
                "totalInsightCount": 11,
                "totalObservations": 165
            }
        }
    }
}

Human Readable Output

Low Severity Insights

6 Active of 11 Insights - 165 Observations

Name Description Observations Insight ID Segment By
ASI: Deprecated Tech - Nginx ##### Description
Deprecated versions of Nginx web server that are no longer supported. Running end of life or deprecated hardware or software can open organizations up to potential risks and vulnerabilities as these systems are no longer supported via regular updates and security patches

##### Remediation
Organizations should consider upgrading to supported versions of Nginx to ensure security patches are available.
146 40466 savedfilter_metric_29643
ASI: SSL Certificates Expiring in 30 Days ##### Description
The following SSL Certificates expire within the next 30 days. Expired certificates can lead to critical business functions being unavailable to customers or employees. Expired certificates could prevent customers from accessing your website and negatively impact an organization’s brand.


##### Remediation
Organizations should review these certificates and ensure appropriate policies and procedures are in place to keep SSL certificates up to date.
13 40466 savedfilter_metric_29632

pt-list-my-attack-surfaces


Retrieves the attack surface information of the individual’s account.

Base Command

pt-list-my-attack-surfaces

Input

Argument Name Description Required
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is 1000. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.AttackSurface.id Number ID of the attack surface.
PassiveTotal.AttackSurface.name String Name of the attack surface.
PassiveTotal.AttackSurface.priority.high.observationCount Number Total observations of high priority attack surface.
PassiveTotal.AttackSurface.priority.high.link String Link to the high priority attack surface.
PassiveTotal.AttackSurface.priority.medium.observationCount Number Total observations of medium priority attack surface.
PassiveTotal.AttackSurface.priority.medium.link String Link to the medium priority attack surface.
PassiveTotal.AttackSurface.priority.low.observationCount Number Total observations of low priority attack surface.
PassiveTotal.AttackSurface.priority.low.link String Link to the low priority attack surface.

Command Example


#### Context Example

```json
{
    "PassiveTotal": {
        "AttackSurface": {
            "id": 88256,
            "name": "RiskIQ, Inc.",
            "priority": {
                "high": {
                    "link": "https://api.pt.net/pt/v2/attack-surface/priority/high",
                    "observationCount": 13
                },
                "low": {
                    "link": "https://api.pt.net/pt/v2/attack-surface/priority/low",
                    "observationCount": 165
                },
                "medium": {
                    "link": "https://api.pt.net/pt/v2/attack-surface/priority/medium",
                    "observationCount": 4
                }
            }
        }
    }
}

Human Readable Output

Attack Surface(s)

ID Name High Severity Medium Severity Low Severity
88256 RiskIQ, Inc. 13 observations 4 observations 165 observations

pt-list-third-party-attack-surface


Retrieves the attack surface observations by severity level for the given third-party account.

Base Command

pt-list-third-party-attack-surface

Input

Argument Name Description Required
id Specify the vendor ID to retrieve the attack surface third party information. Optional
page_number Page number for paging through results.

Note: The minimum value supported is 0 and maximum value supported is int32. Default is 0.
Optional
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is int32. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.ThirdParty.id Number ID of the vendor.
PassiveTotal.ThirdParty.name String Name of the vendor.
PassiveTotal.ThirdParty.priority.high.observationCount Number Total observations of high priority attack surface.
PassiveTotal.ThirdParty.priority.high.link String Link to the high priority attack surface.
PassiveTotal.ThirdParty.priority.medium.observationCount Number Total observations of medium priority attack surface.
PassiveTotal.ThirdParty.priority.medium.link String Link to the medium priority attack surface.
PassiveTotal.ThirdParty.priority.low.observationCount Number Total observations of low priority attack surface.
PassiveTotal.ThirdParty.priority.low.link String Link to the low priority attack surface.
PassiveTotal.Summary.ThirdPartyASI.name String The command name.
PassiveTotal.Summary.ThirdPartyASI.totalCount Number Total number of attack surfaces.
PassiveTotal.Summary.ThirdPartyASI.totalPages Number Number of pages.
PassiveTotal.Summary.ThirdPartyASI.nextPage String Link to the next page.

Command Example


#### Context Example

```json
{
    "PassiveTotal": {
        "Summary": {
            "ThirdPartyASI": {
                "name": "pt-list-third-party-attack-surface",
                "totalCount": 2,
                "totalPages": 1
            }
        },
        "ThirdParty": [
            {
                "id": 45998,
                "name": "Mitsubishi Corporation",
                "priority": {
                    "high": {
                        "link": "https://api.pt.net/pt/v2/attack-surface/third-party/45998/priority/high",
                        "observationCount": 3
                    },
                    "low": {
                        "link": "https://api.pt.net/pt/v2/attack-surface/third-party/45998/priority/low",
                        "observationCount": 92
                    },
                    "medium": {
                        "link": "https://api.pt.net/pt/v2/attack-surface/third-party/45998/priority/medium",
                        "observationCount": 35
                    }
                }
            },
            {
                "id": 371662,
                "name": "Aeroflot-Russian Airlines",
                "priority": {
                    "high": {
                        "link": "https://api.pt.net/pt/v2/attack-surface/third-party/371662/priority/high",
                        "observationCount": 7
                    },
                    "low": {
                        "link": "https://api.pt.net/pt/v2/attack-surface/third-party/371662/priority/low",
                        "observationCount": 16
                    },
                    "medium": {
                        "link": "https://api.pt.net/pt/v2/attack-surface/third-party/371662/priority/medium",
                        "observationCount": 8
                    }
                }
            }
        ]
    }
}

Human Readable Output

Attack Surface(s)

ID Name High Severity Medium Severity Low Severity
45998 Mitsubishi Corporation 3 observations 35 observations 92 observations
371662 Aeroflot-Russian Airlines 7 observations 8 observations 16 observations

pt-list-third-party-attack-surface-insights


Retrieves the attack surface insight information of the given third-party account.

Base Command

pt-list-third-party-attack-surface-insights

Input

Argument Name Description Required
id Specify the vendor ID to retrieve the third-party insights information.

Note: To retrieve the list of vendor IDs, execute the “pt-list-third-party-attack-surface” command.
Required
priority Filter the results based on the priority level specified.

Possible values: high, medium, low.
Required
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is 1000. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.Summary.ThirdPartyInsight.activeInsightCount Number Total number of active third party insights.
PassiveTotal.Summary.ThirdPartyInsight.totalInsightCount Number Total number of third party insights.
PassiveTotal.Summary.ThirdPartyInsight.totalObservations Number Total number of third party observations.
PassiveTotal.ThirdParty.id Number Vendor ID associated with the third party insights.
PassiveTotal.ThirdParty.priorityLevel String Priority level of third party insights.
PassiveTotal.ThirdParty.Insight.insight.name String Name of the third party insight.
PassiveTotal.ThirdParty.Insight.insight.description String Description of the third party insight.
PassiveTotal.ThirdParty.Insight.insight.observationCount Number Number of observations for the given third party insight.
PassiveTotal.ThirdParty.Insight.insight.link String Link to the third party insight.
PassiveTotal.ThirdParty.Insight.insight.insightId String ID of the third party insight.
PassiveTotal.ThirdParty.Insight.insight.segmentBy String Segment by of the third party insight.
PassiveTotal.Summary.ThirdPartyInsight.name String The command name.

Command Example

!pt-list-third-party-attack-surface-insights id="45998" priority="low"

Context Example

{
    "PassiveTotal": {
        "Summary": {
            "ThirdPartyInsight": {
                "activeInsightCount": 9,
                "name": "pt-list-third-party-attack-surface-insights",
                "totalInsightCount": 11,
                "totalObservations": 92
            }
        },
        "ThirdParty": {
            "Insight": {
                "insight": [
                    {
                        "description": "Root page assets with any CVSS/CVSS v3 score. ",
                        "insightId": "40466",
                        "link": "https://api.pt.net/pt/v2/attack-surface/third-party/45998/insight/40466?page=0&size=25&groupBy=RISK_CATEGORY&segmentBy=savedfilter_metric_15881",
                        "name": "Affected CVSS Page",
                        "observationCount": 0,
                        "segmentBy": "savedfilter_metric_15881"
                    },
                    {
                        "description": "##### Description \nDeprecated versions of Apache server that are no longer supported.  Running end of life or deprecated software can open organizations up to potential risks and vulnerabilities as these systems are no longer supported via regular updates and security patches.\n\n##### Remediation\nOrganizations should consider upgrading to supported versions of Apache to ensure security patches are available.",
                        "insightId": "40466",
                        "link": "https://api.pt.net/pt/v2/attack-surface/third-party/45998/insight/40466?page=0&size=25&groupBy=RISK_CATEGORY&segmentBy=savedfilter_metric_29644",
                        "name": "ASI: Deprecated Tech - Apache",
                        "observationCount": 2,
                        "segmentBy": "savedfilter_metric_29644"
                    }
                ]
            },
            "id": 45998,
            "priorityLevel": "low"
        }
    }
}

Human Readable Output

Low Severity Insights

9 Active of 11 Insights - 92 Observations

Name Description Observations Insight ID Segment By
ASI: Deprecated Tech - Apache ##### Description
Deprecated versions of Apache server that are no longer supported. Running end of life or deprecated software can open organizations up to potential risks and vulnerabilities as these systems are no longer supported via regular updates and security patches.

##### Remediation
Organizations should consider upgrading to supported versions of Apache to ensure security patches are available.
2 40466 savedfilter_metric_29644
Affected CVSS Page Root page assets with any CVSS/CVSS v3 score. 0 40466 savedfilter_metric_15881

pt-list-my-attack-surface-assets


Retrieves the attack surface asset information of the individual’s account.

Base Command

pt-list-my-attack-surface-assets

Input

Argument Name Description Required
id Specify the insight ID to retrieve the assets.

Note: To retrieve the list of insight IDs, execute the “pt-list-my-attack-surface-insights” command.
Required
segment_by Specify the segment_by to retrieve the assets.

Note: To retrieve the list of segment by, execute the “pt-list-my-attack-surface-insights” command.
Required
page_number Page number for paging through results.

Note: The minimum value supported is 0 and maximum value supported is int32. Default is 0.
Optional
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is int32. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.Summary.Asset.totalCount Number Total number of available assets.
PassiveTotal.Summary.Asset.totalPages Number Number of pages.
PassiveTotal.Summary.Asset.nextPage String Link to the next page.
PassiveTotal.Asset.insightId String Insight ID for which assets are retrieved.
PassiveTotal.Asset.segmentBy String Segment by for which assets are retrieved.
PassiveTotal.Asset.asset.type String Type of the asset.
PassiveTotal.Asset.asset.name String Name of the asset.
PassiveTotal.Asset.asset.firstSeen Date Date & time the record was first seen.
PassiveTotal.Asset.asset.lastSeen Date Date & time the record was most recently observed.
PassiveTotal.Summary.Asset.name String The command name.

Command Example

!pt-list-my-attack-surface-assets id="40466" segment_by="savedfilter_metric_29634"

Context Example

{
    "PassiveTotal": {
        "Asset": {
            "asset": [
                {
                    "firstSeen": "2016-05-25 20:07:40",
                    "lastSeen": "2021-09-19 09:50:32",
                    "name": "financialtradie.com",
                    "type": "DOMAIN"
                }
            ],
            "insightId": "40466",
            "segmentBy": "savedfilter_metric_29634"
        },
        "Summary": {
            "Asset": {
                "name": "pt-list-my-attack-surface-assets",
                "totalCount": 1,
                "totalPages": 1
            }
        }
    }
}

Human Readable Output

Asset(s)

Name Type First Seen (GMT) Last Seen (GMT)
financialtradie.com DOMAIN 2016-05-25 20:07:40 2021-09-19 09:50:32

pt-list-my-attack-surface-vulnerable-components


Retrieves the attack surface vulnerable component information of the individual’s account.

Base Command

pt-list-my-attack-surface-vulnerable-components

Input

Argument Name Description Required
page_number Page number for paging through results.

Note: The minimum value supported is 0 and maximum value supported is int32. Default is 0.
Optional
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is int32. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.Summary.VulnerableComponent.name String The command name.
PassiveTotal.Summary.VulnerableComponent.totalCount Number Total number of available vulnerable components.
PassiveTotal.Summary.VulnerableComponent.totalPages Number Number of pages.
PassiveTotal.Summary.VulnerableComponent.nextPage String Link to the next page.
PassiveTotal.VulnerableComponent.name String Name of the vulnerable component.
PassiveTotal.VulnerableComponent.type String Type of the vulnerable component.
PassiveTotal.VulnerableComponent.severity String Severity of the vulnerable component.
PassiveTotal.VulnerableComponent.count Number Number of assets affected.

Command Example

!pt-list-my-attack-surface-vulnerable-components page_size=2

Context Example

{
    "PassiveTotal": {
        "Summary": {
            "VulnerableComponent": {
                "name": "pt-list-my-attack-surface-vulnerable-components",
                "nextPage": "https://api.pt.net/pt/v2/attack-surface/vuln-intel/components?page=1&size=2",
                "totalCount": 24,
                "totalPages": 12
            }
        },
        "VulnerableComponent": [
            {
                "count": 129,
                "name": "nginx 1.14.0",
                "severity": "HIGH",
                "type": "Server"
            },
            {
                "count": 13,
                "name": "nginx 1.16.1",
                "severity": "HIGH",
                "type": "Server"
            }
        ]
    }
}

Human Readable Output

Vulnerable Component(s)

Name Type Severity Asset Count
nginx 1.14.0 Server HIGH 129
nginx 1.16.1 Server HIGH 13

pt-list-my-attack-surface-vulnerabilities


Retrieves the attack surface vulnerability information of the individual’s account.

Base Command

pt-list-my-attack-surface-vulnerabilities

Input

Argument Name Description Required
page_number Page number for paging through results.

Note: The minimum value supported is 0 and maximum value supported is int32. Default is 0.
Optional
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is int32. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.Vulnerability.cveId String ID of the CVE.
PassiveTotal.Vulnerability.cwes.cweId String CWE ID associated with the CVE.
PassiveTotal.Vulnerability.priorityScore Number Priority score of the CVE.
PassiveTotal.Vulnerability.observationCount Number Number of observations of CVE.
PassiveTotal.Vulnerability.cveLink String Link to the CVE.
PassiveTotal.Summary.Vulnerability.name String The command name.
PassiveTotal.Summary.Vulnerability.totalCount Number Total number of vulnerabilities.
PassiveTotal.Summary.Vulnerability.totalPages Number Number of pages.
PassiveTotal.Summary.Vulnerability.nextPage String Link to the next page.

Command Example

!pt-list-my-attack-surface-vulnerabilities page_size=2

Context Example

{
    "PassiveTotal": {
        "Summary": {
            "Vulnerability": {
                "name": "pt-list-my-attack-surface-vulnerabilities",
                "nextPage": "https://api.pt.net/pt/v2/attack-surface/vuln-intel/cves?page=1&size=2",
                "totalCount": 413,
                "totalPages": 207
            }
        },
        "Vulnerability": [
            {
                "cveId": "CVE-2021-23017",
                "cveLink": "https://api.pt.net/pt/v2/vuln-intel/CVE-2021-23017",
                "cwes": [
                    {
                        "cweId": "CWE-193"
                    }
                ],
                "observationCount": 149,
                "priorityScore": 98
            },
            {
                "cveId": "CVE-2019-20372",
                "cveLink": "https://api.pt.net/pt/v2/vuln-intel/CVE-2019-20372",
                "cwes": [
                    {
                        "cweId": "CWE-444"
                    }
                ],
                "observationCount": 145,
                "priorityScore": 53
            }
        ]
    }
}

Human Readable Output

Vulnerabilities

CVE ID CWE ID RiskIQ Priority Score Asset Count
CVE-2021-23017 CWE-193 98.0 149
CVE-2019-20372 CWE-444 53.0 145

pt-list-my-attack-surface-observations


Retrieves the attack surface vulnerability observation information of the individual’s account.

Base Command

pt-list-my-attack-surface-observations

Input

Argument Name Description Required
cve_id Specify the CVE ID to retrieve observations of that CVE.

Note: To retrieve the list of CVE IDs, execute the “pt-list-my-attack-surface-vulnerabilities” command.
Required
page_number Page number for paging through results.

Note: The minimum value supported is 0 and maximum value supported is int32. Default is 0.
Optional
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is int32. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.Observation.asset.type String Type of the asset.
PassiveTotal.Observation.asset.name String Name of the asset.
PassiveTotal.Observation.asset.firstSeen Date Date & time the record was first seen.
PassiveTotal.Observation.asset.lastSeen Date Date & time the record was most recently observed.
PassiveTotal.Observation.cveId String ID of the CVE.
PassiveTotal.Observation.cwe.cweId String CWE ID associated with the CVE.
PassiveTotal.Summary.Observation.name String The command name.
PassiveTotal.Summary.Observation.totalCount Number Total number of vulnerabilities.
PassiveTotal.Summary.Observation.totalPages Number Number of pages.
PassiveTotal.Summary.Observation.nextPage String Link to the next page.

Command Example

!pt-list-my-attack-surface-observations cve_id="CVE-2021-23017" page_size=2

Context Example

{
    "PassiveTotal": {
        "Observation": {
            "asset": [
                {
                    "firstSeen": "2018-05-11 20:40:17",
                    "lastSeen": "2021-09-19 14:46:48",
                    "name": "riskiq.app",
                    "type": "HOST"
                },
                {
                    "firstSeen": "2018-06-30 00:03:32",
                    "lastSeen": "2021-09-15 19:36:38",
                    "name": "www.riskiq.app",
                    "type": "HOST"
                }
            ],
            "cveId": "CVE-2021-23017",
            "cwe": [
                {
                    "cweId": "CWE-193"
                }
            ]
        },
        "Summary": {
            "Observation": {
                "name": "pt-list-my-attack-surface-observations",
                "nextPage": "https://api.pt.net/pt/v2/attack-surface/vuln-intel/cves/CVE-2021-23017/observations?page=1&size=2",
                "totalCount": 149,
                "totalPages": 75
            }
        }
    }
}

Human Readable Output

Observation(s)

Name Type First Seen (GMT) Last Seen (GMT)
riskiq.app HOST 2018-05-11 20:40:17 2021-09-19 14:46:48
www.riskiq.app HOST 2018-06-30 00:03:32 2021-09-15 19:36:38

pt-list-third-party-attack-surface-assets


Retrieves the attack surface asset information of the given third-party account.

Base Command

pt-list-third-party-attack-surface-assets

Input

Argument Name Description Required
id Specify the insight ID to retrieve the assets.

Note: To retrieve the list of insight IDs, execute the “pt-list-third-party-attack-surface-insights” command.
Required
vendor_id Specify the vendor ID to retrieve the assets of a specific vendor.

Note: To retrieve the list of vendor IDs, execute the “pt-list-third-party-attack-surface” command.
Required
segment_by Specify the segment_by to retrieve the assets.

Note: To retrieve the list of segment by, execute the “pt-list-third-party-attack-surface-insights” command.
Required
page_number Page number for paging through results.

Note: The minimum value supported is 0 and maximum value supported is int32. Default is 0.
Optional
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is int32. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.ThirdParty.id Number ID of the vendor.
PassiveTotal.Summary.ThirdPartyInsightAsset.name String The command name.
PassiveTotal.Summary.ThirdPartyInsightAsset.totalCount Number Total number of available assets.
PassiveTotal.Summary.ThirdPartyInsightAsset.totalPages Number Number of pages.
PassiveTotal.Summary.ThirdPartyInsightAsset.nextPage String Link to the next page.
PassiveTotal.ThirdParty.InsightAsset.insightId Number Insight ID for which assets are retrieved.
PassiveTotal.ThirdParty.InsightAsset.segmentBy String Segment by for which assets are retrieved.
PassiveTotal.ThirdParty.InsightAsset.asset.type String Type of the asset.
PassiveTotal.ThirdParty.InsightAsset.asset.name String Name of the asset.
PassiveTotal.ThirdParty.InsightAsset.asset.firstSeen Date Date & time the record was first seen.
PassiveTotal.ThirdParty.InsightAsset.asset.lastSeen Date Date & time the record was most recently observed.

Command Example

!pt-list-third-party-attack-surface-assets id="40464" vendor_id="45998" segment_by="savedfilter_metric_29644"

Context Example

{
    "PassiveTotal": {
        "Summary": {
            "ThirdPartyInsightAsset": {
                "name": "pt-list-third-party-attack-surface-assets",
                "totalCount": 2,
                "totalPages": 1
            }
        },
        "ThirdParty": {
            "InsightAsset": {
                "asset": [
                    {
                        "firstSeen": "2010-06-24 07:49:19",
                        "lastSeen": "2021-09-21 23:02:08",
                        "name": "160.189.11.4",
                        "type": "IP_ADDRESS"
                    },
                    {
                        "firstSeen": "2017-12-01 09:22:21",
                        "lastSeen": "2021-09-21 18:23:10",
                        "name": "ec.soup-stock-tokyo.com",
                        "type": "HOST"
                    }
                ],
                "insightId": "40464",
                "segmentBy": "savedfilter_metric_29644"
            },
            "id": 45998
        }
    }
}

Human Readable Output

Asset(s)

Name Type First Seen (GMT) Last Seen (GMT)
160.189.11.4 IP_ADDRESS 2010-06-24 07:49:19 2021-09-21 23:02:08
ec.soup-stock-tokyo.com HOST 2017-12-01 09:22:21 2021-09-21 18:23:10

pt-list-third-party-attack-surface-vulnerable-components


Retrieves the attack surface vulnerable component information of the given third-party account.

Base Command

pt-list-third-party-attack-surface-vulnerable-components

Input

Argument Name Description Required
id Specify the vendor ID to retrieve the vulnerable components for a particular vendor.

Note: To retrieve the list of vendor IDs, execute the “pt-list-third-party-attack-surface” command.
Required
page_number Page number for paging through results.

Note: The minimum value supported is 0 and maximum value supported is int32. Default is 0.
Optional
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is int32. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.ThirdParty.id String ID of the vendor.
PassiveTotal.Summary.ThirdPartyVulnerableComponent.name String The command name.
PassiveTotal.Summary.ThirdPartyVulnerableComponent.totalCount Number Total number of available vulnerable components.
PassiveTotal.Summary.ThirdPartyVulnerableComponent.totalPages Number Number of pages.
PassiveTotal.Summary.ThirdPartyVulnerableComponent.nextPage String Link to the next page.
PassiveTotal.ThirdParty.VulnerableComponent.name String Name of the vulnerable component.
PassiveTotal.ThirdParty.VulnerableComponent.type String Type of the vulnerable component.
PassiveTotal.ThirdParty.VulnerableComponent.severity String Severity of the vulnerable component.
PassiveTotal.ThirdParty.VulnerableComponent.count Number Number of assets affected.

Command Example

!pt-list-third-party-attack-surface-vulnerable-components id="45998" page_size=2

Context Example

{
    "PassiveTotal": {
        "Summary": {
            "ThirdPartyVulnerableComponent": {
                "name": "pt-list-third-party-attack-surface-vulnerable-components",
                "nextPage": "https://api.pt.net/pt/v2/attack-surface/vuln-intel/third-party/45998/components?page=1&size=2",
                "totalCount": 45,
                "totalPages": 23
            }
        },
        "ThirdParty": {
            "VulnerableComponent": [
                {
                    "count": 14,
                    "name": "Microsoft-IIS 8.5",
                    "severity": "MEDIUM",
                    "type": "Server"
                },
                {
                    "count": 5,
                    "name": "OpenSSL 1.0.2k",
                    "severity": "HIGH",
                    "type": "Server Module"
                }
            ],
            "id": 45998
        }
    }
}

Human Readable Output

Vulnerable Component(s)

Name Type Severity Asset Count
Microsoft-IIS 8.5 Server MEDIUM 14
OpenSSL 1.0.2k Server Module HIGH 5

pt-list-third-party-attack-surface-vulnerabilities


Retrieves the attack surface vulnerability information of the given third-party account.

Base Command

pt-list-third-party-attack-surface-vulnerabilities

Input

Argument Name Description Required
id Specify the vendor ID to retrieve the vulnerabilities for a particular vendor.

Note: To retrieve the list of vendor IDs, execute the “pt-list-third-party-attack-surface” command.
Required
page_number Page number for paging through results.

Note: The minimum value supported is 0 and maximum value supported is int32. Default is 0.
Optional
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is int32. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.ThirdParty.id Number ID of the vendor.
PassiveTotal.ThirdParty.Vulnerability.cveId String ID of the CVE.
PassiveTotal.ThirdParty.Vulnerability.cwes.cweId String CWE ID associated with the CVE.
PassiveTotal.ThirdParty.Vulnerability.priorityScore Number Priority score of the CVE.
PassiveTotal.ThirdParty.Vulnerability.observationCount Number Number of observations of CVE.
PassiveTotal.ThirdParty.Vulnerability.cveLink String Link to the CVE.
PassiveTotal.Summary.ThirdPartyVulnerability.name String The command name.
PassiveTotal.Summary.ThirdPartyVulnerability.totalCount Number Total number of vulnerabilities.
PassiveTotal.Summary.ThirdPartyVulnerability.totalPages Number Number of pages.
PassiveTotal.Summary.ThirdPartyVulnerability.nextPage String Link to the next page.

Command Example

!pt-list-third-party-attack-surface-vulnerabilities id="45998" page_size=2

Context Example

{
    "PassiveTotal": {
        "Summary": {
            "ThirdPartyVulnerability": {
                "name": "pt-list-third-party-attack-surface-vulnerabilities",
                "nextPage": "https://api.pt.net/pt/v2/attack-surface/vuln-intel/third-party/45998/cves?page=1&size=2",
                "totalCount": 548,
                "totalPages": 274
            }
        },
        "ThirdParty": {
            "Vulnerability": [
                {
                    "cveId": "CVE-2020-11022",
                    "cveLink": "https://api.pt.net/pt/v2/vuln-intel/CVE-2020-11022",
                    "cwes": [
                        {
                            "cweId": "CWE-79"
                        }
                    ],
                    "observationCount": 19,
                    "priorityScore": 61
                },
                {
                    "cveId": "CVE-2020-11023",
                    "cveLink": "https://api.pt.net/pt/v2/vuln-intel/CVE-2020-11023",
                    "cwes": [
                        {
                            "cweId": "CWE-79"
                        }
                    ],
                    "observationCount": 19,
                    "priorityScore": 61
                }
            ],
            "id": 45998
        }
    }
}

Human Readable Output

Vulnerabilities

CVE ID CWE ID RiskIQ Priority Score Asset Count
CVE-2020-11022 CWE-79 61.0 19
CVE-2020-11023 CWE-79 61.0 19

pt-list-third-party-attack-surface-observations


Retrieves the attack surface vulnerability observation information of the given third-party account.

Base Command

pt-list-third-party-attack-surface-observations

Input

Argument Name Description Required
id Specify the vendor ID to retrieve the vulnerability observations for a particular vendor.

Note: To retrieve the list of vendor IDs, execute the “pt-list-third-party-attack-surface” command.
Required
cve_id Specify the CVE ID to retrieve observations of the CVE.

Note: To retrieve the list of CVE IDs, execute the “pt-list-third-party-attack-surface-vulnerabilities” command.
Required
page_number Page number for paging through results.

Note: The minimum value supported is 0 and maximum value supported is int32. Default is 0.
Optional
page_size Maximum number of results to return per page.

Note: The minimum value supported is 1 and maximum value supported is int32. Default is 50.
Optional

Context Output

Path Type Description
PassiveTotal.ThirdParty.id Number ID of the vendor.
PassiveTotal.ThirdParty.Observation.asset.type String Type of the asset.
PassiveTotal.ThirdParty.Observation.asset.name String Name of the asset.
PassiveTotal.ThirdParty.Observation.asset.firstSeen Date Date & time the record was first seen.
PassiveTotal.ThirdParty.Observation.asset.lastSeen Date Date & time the record was most recently observed.
PassiveTotal.ThirdParty.Observation.cveId String ID of the CVE.
PassiveTotal.ThirdParty.Observation.cwe.cweId String CWE ID associated with the CVE.
PassiveTotal.Summary.ThirdPartyObservation.name String The command name.
PassiveTotal.Summary.ThirdPartyObservation.totalCount Number Total number of observations.
PassiveTotal.Summary.ThirdPartyObservation.totalPages Number Number of pages.
PassiveTotal.Summary.ThirdPartyObservation.nextPage String Link to the next page.

Command Example

!pt-list-third-party-attack-surface-observations id="45998" cve_id="CVE-2020-11022" page_size=2

Context Example

{
    "PassiveTotal": {
        "Summary": {
            "ThirdPartyObservation": {
                "name": "pt-list-third-party-attack-surface-observations",
                "nextPage": "https://api.pt.net/pt/v2/attack-surface/vuln-intel/third-party/45998/cves/CVE-2020-11022/observations?page=1&size=2",
                "totalCount": 19,
                "totalPages": 10
            }
        },
        "ThirdParty": {
            "Observation": {
                "asset": [
                    {
                        "firstSeen": "2015-05-12 14:58:34",
                        "lastSeen": "2021-09-21 06:14:39",
                        "name": "blog.accesstage.com.br",
                        "type": "HOST"
                    },
                    {
                        "firstSeen": "2010-09-22 14:57:20",
                        "lastSeen": "2021-09-22 00:21:45",
                        "name": "www.accesstage.com.br",
                        "type": "HOST"
                    }
                ],
                "cveId": "CVE-2020-11022",
                "cwe": [
                    {
                        "cweId": "CWE-79"
                    }
                ]
            },
            "id": 45998
        }
    }
}

Human Readable Output

Observation(s)

Name Type First Seen (GMT) Last Seen (GMT)
blog.accesstage.com.br HOST 2015-05-12 14:58:34 2021-09-21 06:14:39
www.accesstage.com.br HOST 2010-09-22 14:57:20 2021-09-22 00:21:45

Configuration parameters

  • url — API URL (required)
  • credentials — Username
  • username — Username
  • secret — API Secret
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • request_timeout — HTTP(S) Request Timeout (in seconds)
  • integrationReliability — Source Reliability
  • feedExpirationPolicy
  • feedExpirationInterval

Commands (28)

  • domain

    Provides data enrichment for domains.

  • ip

    Checks the reputation of an IP address.

  • pt-get-articles

    Retrieves information related to articles for a specific indicator.

  • pt-get-components

    Retrieves the host attribute components for a domain or IP address. Maximum 2000 records are fetched.

  • pt-get-cookies

    Retrieves cookies addresses or hostname information based on cookie name or domain.

  • pt-get-data-card

    Retrieves a summary data card associated with the given query.

  • pt-get-host-pairs

    Retrieves the host attribute pairs related to a domain or IP address. Maximum 2000 records are fetched.

  • pt-get-pdns-details

    Retrieves the passive DNS results from active account sources.

  • pt-get-reputation

    Gets reputation for a given domain, host or IP.

  • pt-get-services

    Retrieves exposed services on the recently open ports for an IP address.

  • pt-get-trackers

    Retrieves the host attribute trackers for a domain or IP address. Maximum 2000 records are fetched.

  • pt-get-whois

    Gets WHOIS information records based on queries.

  • pt-list-intel-profile-indicators

    Retrieves the indicators for the given profile.

  • pt-list-intel-profiles

    Retrieves the list of all profiles.

  • pt-list-my-attack-surface-assets

    Retrieves the attack surface asset information of the individual's account.

  • pt-list-my-attack-surface-insights

    Retrieves the attack surface insight information of the individual's account.

  • pt-list-my-attack-surface-observations

    Retrieves the attack surface vulnerability observation information of the individual's account.

  • pt-list-my-attack-surface-vulnerabilities

    Retrieves the attack surface vulnerability information of the individual's account.

  • pt-list-my-attack-surface-vulnerable-components

    Retrieves the attack surface vulnerable component information of the individual's account.

  • pt-list-my-attack-surfaces

    Retrieves the attack surface information of the individual's account.

  • pt-list-third-party-attack-surface

    Retrieves the attack surface observations by severity level for the given third-party account.

  • pt-list-third-party-attack-surface-assets

    Retrieves the attack surface asset information of the given third-party account.

  • pt-list-third-party-attack-surface-insights

    Retrieves the attack surface insight information of the given third-party account.

  • pt-list-third-party-attack-surface-observations

    Retrieves the attack surface vulnerability observation information of the given third-party account.

  • pt-list-third-party-attack-surface-vulnerabilities

    Retrieves the attack surface vulnerability information of the given third-party account.

  • pt-list-third-party-attack-surface-vulnerable-components

    Retrieves the attack surface vulnerable component information of the given third-party account.

  • pt-ssl-cert-search

    Retrieves SSL certificates for a given field value.

  • pt-whois-search

    Gets WHOIS information records based on field matching queries.

from CommonServerPython import *

""" IMPORTS """

import re
import traceback
from typing import Any
from urllib import parse

import urllib3
from requests import ConnectionError
from requests.exceptions import InvalidSchema, MissingSchema

# Disable insecure warnings
urllib3.disable_warnings()

""" CONSTANTS """
DATE_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"

DATE_FORMAT = "%Y-%m-%d"

INTEGRATION_NAME: str = "PassiveTotal"

DEFAULT_REQUEST_TIMEOUT = "20"

PROFILE_TYPE = ["actor", "backdoor", "tool"]

SOURCE = ["osint", "riskiq"]

DEFAULT_PAGE_NUMBER = 0

DEFAULT_SIZE = 50

MESSAGES: dict[str, str] = {
    "AUTHENTICATION_ERROR": "Unauthenticated. Check the configured Username and API secret.",
    "PAGE_NOT_FOUND_ERROR": "No record(s) found.",
    "INTERNAL_SERVER_ERROR": "The server encountered an internal error for PassiveTotal and was unable to complete "
    "your request.",
    "BAD_REQUEST_ERROR": "An error occurred while fetching the data.",
    "REQUEST_TIMEOUT_VALIDATION": "HTTP(S) Request timeout parameter must be a positive integer.",
    "REQUEST_TIMEOUT_EXCEED_ERROR": "Value is too large for HTTP(S) Request Timeout.",
    "NO_RECORDS_FOUND": "No {0} were found for the given argument(s).",
    "EMPTY_WHOIS_ARGUMENT": "query or field argument should not be empty.",
    "EMPTY_DOMAIN_ARGUMENT": "domain argument should not be empty.",
    "INVALID_DIRECTION_VALUE": "The given value for direction is invalid. Supported values: children, parents.",
    "INVALID_QUERY_VALUE": "The given value for query is invalid.",
    "MISSING_SCHEMA_ERROR": "Invalid API URL. No schema supplied: http(s).",
    "INVALID_SCHEMA_ERROR": "Invalid API URL. Supplied schema is invalid, supports http(s).",
    "CONNECTION_ERROR": "Connectivity failed. Check your internet connection, the API URL or try increasing the HTTP(s)"
    " Request Timeout.",
    "PROXY_ERROR": "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_VALUE_IN_FIELD_ARG": "Invalid field type {}. Valid field types are domain, email, name, organization, "
    "address, phone, nameserver.",
    "INVALID_IP": "IP is not valid. Please enter a valid IP address.",
    "EMPTY_GET_WHOIS_ARGUMENT": "query argument should not be empty.",
    "INVALID_VALUE_IN_HISTORY_ARG": "Invalid history value {}. Valid history values are true, false.",
    "INVALID_WHOLE_NUMBER": "Argument {} should be 0 or a positive integer.",
    "INVALID_SINGLE_SELECT": "Invalid argument {}. Valid values are {}.",
    "INVALID_QUERY_COOKIE_DOMAIN": "Argument query should be a valid domain that "
    "does not contain any special characters other than hyphen (-) and full stop (.)",
    "INVALID_QUERY_COOKIE_NAME": "Argument query should be a valid cookie name that does not contain "
    "spaces, separator character or tabs.",
    "REQUIRED_ARGUMENT": "Invalid argument value. {} is a required argument.",
    "EMPTY_IP_ARGUMENT": "IP(s) not specified",
    "INVALID_IP_ARGUMENT": "Invalid IP - {}",
    "INVALID_INDICATOR_TYPE": "Invalid indicator type {}. Valid values are certficate_sha1, certificate_sha1, domain, "
    "email, hash_md5, hash_sha256, ip, pdb_path, soa_email, url, whois_email.",
    "INVALID_SOURCE": "Invalid indicator source {}. Valid values are osint, riskiq.",
    "INVALID_PAGE_SIZE": "{} is an invalid value for page size. Page size must be between 1 and int32.",
    "INVALID_PAGE_NUMBER": "{} is an invalid value for page number. Page number must be between 0 and int32.",
    "INVALID_PROFILE_TYPE": "Invalid profile type {}. Valid profile types are actor, backdoor, tool.",
    "REQUIRED_INDICATOR_VALUE": "'indicator_value' must be specified if the arguments 'source' or 'category' are used.",
    "INVALID_PRIORITY_LEVEL": "Invalid priority level {}. Valid priority level are low, medium, high.",
    "NOT_VALID_PAGE_SIZE": "{} is an invalid value for page size. Page size must be between 1 and 1000.",
}

URL_SUFFIX: dict[str, str] = {
    "TEST_MODULE": "/v2/account",
    "SSL_CERT_SEARCH": "/v2/ssl-certificate/search",
    "GET_PDNS_DETAILS": "/v2/dns/passive",
    "WHOIS_SEARCH": "/v2/whois/search",
    "WHOIS_GET": "/v2/whois",
    "GET_COMPONENTS": "/v2/host-attributes/components",
    "GET_TRACKERS": "/v2/host-attributes/trackers",
    "GET_ARTICLES": "/v2/articles/indicator",
    "GET_HOST_PAIRS": "/v2/host-attributes/pairs",
    "GET_SERVICES": "/v2/services",
    "GET_ADDRESSES_BY_COOKIE_NAME": "v2/cookies/name/{0}/addresses",
    "GET_ADDRESSES_BY_COOKIE_DOMAIN": "v2/cookies/domain/{0}/addresses",
    "GET_HOSTS_BY_COOKIE_NAME": "v2/cookies/name/{0}/hosts",
    "GET_HOSTS_BY_COOKIE_DOMAIN": "v2/cookies/domain/{0}/hosts",
    "GET_DATA_CARD_SUMMARY": "v2/cards/summary",
    "GET_REPUTATION": "v2/reputation",
    "LIST_INTEL_PROFILE_INDICATOR": "v2/intel-profiles/{}/indicators",
    "LIST_INTEL_PROFILE": "v2/intel-profiles",
    "LIST_INTEL_PROFILE_BY_INDICATOR": "v2/intel-profiles/indicator",
    "LIST_ASI_INSIGHTS": "/v2/attack-surface/priority",
    "LIST_ATTACK_SURFACE": "v2/attack-surface",
    "LIST_THIRD_PARTY_ASI": "v2/attack-surface/third-party",
    "LIST_ASI_ASSETS": "v2/attack-surface/insight",
    "THIRD_PARTY_ASI_INSIGHTS": "v2/attack-surface/third-party/{}/priority/{}",
    "LIST_ASI_VULNERABLE_COMPONENTS": "v2/attack-surface/vuln-intel/components",
    "LIST_ASI_VULNERABILITIES": "v2/attack-surface/vuln-intel/cves",
    "LIST_ASI_OBSERVATIONS": "v2/attack-surface/vuln-intel/cves/{}/observations",
    "LIST_THIRD_PARTY_ASI_ASSETS": "v2/attack-surface/third-party/{}/insight/{}",
    "LIST_THIRD_PARTY_ASI_VULNERABLE_COMPONENTS": "v2/attack-surface/vuln-intel/third-party/{}/components",
    "LIST_THIRD_PARTY_ASI_VULNERABILITIES": "v2/attack-surface/vuln-intel/third-party/{}/cves",
    "LIST_THIRD_PARTY_ASI_OBSERVATIONS": "v2/attack-surface/vuln-intel/third-party/{}/cves/{}/observations",
}

ISO_DATE: dict[str, str] = {DATE_TIME_FORMAT: '"yyyy-mm-dd hh:mm:ss"', DATE_FORMAT: '"yyyy-mm-dd"'}

VALID_DIRECTION_FOR_HOST_PAIRS = ["children", "parents"]

REQUEST_TIMEOUT_MAX_VALUE = 9223372036

COMPANY_NAME = "PaloAltoNetworks"

PRODUCT_NAME = "XSOAR"

INDICATOR_TYPE = [
    "certficate_sha1",
    "certificate_sha1",
    "domain",
    "email",
    "hash_md5",
    "hash_sha256",
    "ip",
    "pdb_path",
    "soa_email",
    "url",
    "whois_email",
]

PRIORITY = ["high", "medium", "low"]

UI_LINK = "https://community.riskiq.com/attack-surfaces/"


class Client(BaseClient):
    """
    Client to use in integration with powerful http_request.
    It extends the base client and uses the http_request method for the API request.
    Handle some exceptions externally.
    """

    def __init__(self, base_url, request_timeout, verify, proxy, auth):
        super().__init__(base_url=base_url, verify=verify, proxy=proxy, auth=auth)
        self.request_timeout = request_timeout

    def http_request(self, method: str, url_suffix: str, json_data=None, params=None, headers=None) -> dict[str, Any]:
        """
            Override http_request method from BaseClient class. This method will print an error based on status code
            and exceptions.

        :type method: ``str``
        :param method: The HTTP method, for example: GET, POST, and so on.

        :type url_suffix: ``str``
        :param url_suffix: The API endpoint.

        :type json_data: ``dict``
        :param json_data: The dictionary to send in a 'POST' request.

        :type params: ``dict``
        :param params: URL parameters to specify the query.

        :type headers: ``dict``
        :param headers: Headers to send in the request. If None, will use self._headers.

        :return: Depends on the resp_type parameter
        :rtype: ``dict`` or ``str`` or ``requests.Response``
        """
        try:
            if not headers:
                headers = {}
            headers["X-RISKIQ"] = f"{COMPANY_NAME}-{PRODUCT_NAME}-{get_demisto_version_as_str()}"

            resp = self._http_request(
                method=method,
                url_suffix=url_suffix,
                json_data=json_data,
                params=params,
                headers=headers,
                timeout=self.request_timeout,
                resp_type="response",
                ok_codes=(200, 400, 401, 404, 407, 500),
                proxies=handle_proxy(),
            )
        except MissingSchema:
            raise ValueError(MESSAGES["MISSING_SCHEMA_ERROR"])
        except InvalidSchema:
            raise ValueError(MESSAGES["INVALID_SCHEMA_ERROR"])
        except DemistoException as e:
            if "Proxy Error" in str(e):
                raise ConnectionError(MESSAGES["PROXY_ERROR"])
            elif "ConnectionError" in str(e) or "ConnectTimeout" in str(e):
                raise ConnectionError(MESSAGES["CONNECTION_ERROR"])
            else:
                raise e

        # Providing errors based on status code
        status_code = resp.status_code

        if status_code != 200:
            error_message = ""
            if resp.json().get("message", ""):
                error_message = "Reason: {}".format(resp.json().get("message", ""))
            status_code_message_map = {
                400: "{} {}".format(MESSAGES["BAD_REQUEST_ERROR"], error_message),
                401: MESSAGES["AUTHENTICATION_ERROR"],
                404: MESSAGES["PAGE_NOT_FOUND_ERROR"],
                407: MESSAGES["PROXY_ERROR"],
            }
            if status_code in status_code_message_map:
                demisto.info(f"Response code: {status_code}. Reason: {status_code_message_map[status_code]}")
                raise ValueError(status_code_message_map[status_code])
            elif status_code >= 500:
                demisto.info("Response code: {}. Reason: {}".format(status_code, MESSAGES["INTERNAL_SERVER_ERROR"]))
                raise ValueError(MESSAGES["INTERNAL_SERVER_ERROR"])
            else:
                resp.raise_for_status()

        return resp.json()


""" HELPER FUNCTIONS """


def get_request_timeout() -> Any | None:
    """
    Validate and return the request timeout parameter.
    The parameter must be a positive integer.
    Default value is set to 20 seconds for API request timeout.

    :params req_timeout: Request timeout value.
    :return: boolean
    """
    try:
        request_timeout = demisto.params().get("request_timeout", DEFAULT_REQUEST_TIMEOUT)
        request_timeout = request_timeout if request_timeout else DEFAULT_REQUEST_TIMEOUT
        request_timeout = int(request_timeout)
    except ValueError:
        raise ValueError(MESSAGES["REQUEST_TIMEOUT_VALIDATION"])

    if request_timeout <= 0:
        raise ValueError(MESSAGES["REQUEST_TIMEOUT_VALIDATION"])
    elif request_timeout > REQUEST_TIMEOUT_MAX_VALUE:
        raise ValueError(MESSAGES["REQUEST_TIMEOUT_EXCEED_ERROR"])

    return request_timeout


def remove_empty_elements_for_context(src):
    """
     Recursively remove empty lists, empty dicts, empty string or None elements from a dictionary.

    :type src: ``dict``
    :param src: Input dictionary.

    :return: Dictionary with all empty lists,empty string and empty dictionaries removed.
    :rtype: ``dict``
    """

    def empty(x):
        return x is None or x == "" or x == {} or x == []

    if not isinstance(src, dict | list):
        return src
    elif isinstance(src, list):
        return [v for v in (remove_empty_elements_for_context(v) for v in src) if not empty(v)]
    else:
        return {k: v for k, v in ((k, remove_empty_elements_for_context(v)) for k, v in src.items()) if not empty(v)}


def get_host_attribute_context_data(records: list[dict[str, Any]]) -> tuple[list, list]:
    """
    Prepares context data for get components and get trackers command

    :param records: API response
    :return: standard entry command results list and custom entry context
    """
    custom_ec = createContext(data=records, removeNull=True)
    standard_results: list[CommandResults] = []
    for record in records:
        if record.get("hostname"):
            hostname = record.get("hostname")
            dbot_score = Common.DBotScore(
                indicator=hostname,
                indicator_type=DBotScoreType.DOMAIN,
                integration_name=INTEGRATION_NAME,
                score=Common.DBotScore.NONE,
                reliability=demisto.params().get("integrationReliability"),
            )
            if auto_detect_indicator_type(hostname) == FeedIndicatorType.Domain:
                domain_ioc = Common.Domain(domain=hostname, dbot_score=dbot_score)
                # add standard output with standard readable output
                standard_results.append(
                    CommandResults(
                        indicator=domain_ioc,
                        readable_output=tableToMarkdown("", domain_ioc.to_context().get(Common.Domain.CONTEXT_PATH)),
                    )
                )
        elif record.get("address"):
            address = record.get("address")
            dbot_score = Common.DBotScore(
                indicator=address,
                indicator_type=DBotScoreType.IP,
                integration_name=INTEGRATION_NAME,
                score=Common.DBotScore.NONE,
                reliability=demisto.params().get("integrationReliability"),
            )
            if auto_detect_indicator_type(address) == FeedIndicatorType.IP:
                ip_ioc = Common.IP(ip=address, dbot_score=dbot_score)
                # add standard output with standard readable output
                standard_results.append(
                    CommandResults(
                        indicator=ip_ioc, readable_output=tableToMarkdown("", ip_ioc.to_context().get(Common.IP.CONTEXT_PATH))
                    )
                )

    return standard_results, custom_ec


def get_components_hr(components: list[dict[str, Any]]) -> str:
    """
    Prepares human readable text for get components command

    :param components: Components data response
    :return: Human readable output for components
    """

    hr_table: list[dict[str, Any]] = []

    for component in components:
        hr_row = {
            "Hostname": component.get("hostname", ""),
            "Address": component.get("address", ""),
            "First (GMT)": component.get("firstSeen", ""),
            "Last (GMT)": component.get("lastSeen", ""),
            "Category": component.get("category", ""),
            "Value": component.get("label", ""),
            "Version": component.get("version", ""),
        }
        hr_table.append(hr_row)

    hr_headers = ["Hostname", "Address", "First (GMT)", "Last (GMT)", "Category", "Value", "Version"]
    hr = f"### Total Retrieved Record(s): {len(components)}\n"
    return hr + tableToMarkdown("COMPONENTS", hr_table, hr_headers, removeNull=True)


def get_trackers_hr(trackers: list[dict[str, Any]]) -> str:
    """
    Prepares human readable text for get trackers command

    :param trackers: Trackers data response
    :return: Human readable output for trackers
    """

    hr_table: list[dict[str, Any]] = []

    for tracker in trackers:
        hr_row = {
            "Hostname": tracker.get("hostname", ""),
            "Address": tracker.get("address", ""),
            "First (GMT)": tracker.get("firstSeen", ""),
            "Last (GMT)": tracker.get("lastSeen", ""),
            "Type": tracker.get("attributeType", ""),
            "Value": tracker.get("attributeValue", ""),
        }
        hr_table.append(hr_row)

    hr_headers = ["Hostname", "Address", "First (GMT)", "Last (GMT)", "Type", "Value"]
    hr = f"### Total Retrieved Record(s): {len(trackers)}\n"
    return hr + tableToMarkdown("TRACKERS", hr_table, hr_headers, removeNull=True)


def get_host_pairs_hr(host_pairs: list[dict[str, Any]]) -> str:
    """
    Prepares human readable text for get host pairs command

    :param host_pairs: Host pairs data response
    :return: Human readable output for host pairs
    """

    hr_table: list[dict[str, Any]] = []

    for pair in host_pairs:
        hr_row = {
            "Parent Hostname": pair.get("parent", ""),
            "Child Hostname": pair.get("child", ""),
            "First (GMT)": pair.get("firstSeen", ""),
            "Last (GMT)": pair.get("lastSeen", ""),
            "Cause": pair.get("cause", ""),
        }
        hr_table.append(hr_row)

    hr_headers = ["Parent Hostname", "Child Hostname", "First (GMT)", "Last (GMT)", "Cause"]
    hr = f"### Total Retrieved Record(s): {len(host_pairs)}\n"
    return hr + tableToMarkdown("HOST PAIRS", hr_table, hr_headers, removeNull=True)


def get_common_arguments(args: dict[str, Any]) -> dict[str, Any]:
    """
    Validates the common arguments and prepares parameter dictionary
    This method is used in commands "get-trackers", "get-components", "get-host-pairs"

    :param args: The general arguments
    :return: dict of params or message describing error in argument validation
    """
    params: dict[str, Any] = {}

    if not args.get("query"):
        raise ValueError(MESSAGES["INVALID_QUERY_VALUE"])
    params["query"] = args.get("query")

    date_arguments = ["start", "end"]
    for argument in date_arguments:
        if args.get(argument):
            params[argument] = args.get(argument, "")

    return params


def get_valid_whois_search_arguments(args: dict[str, Any]) -> tuple[str, str]:
    """
    Get and Validate arguments for pt-whois-search command.

    :param args: it contain arguments of pt-whois-search command
    :return: validated arguments 'query' and 'field'
    """
    query = args.get("query", "")
    field = args.get("field", "")
    if query.strip() == "" or field.strip() == "":
        raise ValueError(MESSAGES["EMPTY_WHOIS_ARGUMENT"])
    if field not in ("domain", "email", "name", "organization", "address", "phone", "nameserver"):
        raise ValueError(MESSAGES["INVALID_VALUE_IN_FIELD_ARG"].format(field))
    return query, field


def get_valid_get_whois_arguments(args: dict[str, Any]) -> tuple[str, str]:
    """
    Get and Validate arguments for pt-get-whois command.

    :param args: it contain arguments of pt-get-whois command
    :return: validated arguments 'query' and 'history'
    """
    query = args.get("query", "").strip()
    arg_history = args.get("history", "").strip()
    history = arg_history.lower()
    if query == "":
        raise ValueError(MESSAGES["EMPTY_GET_WHOIS_ARGUMENT"])
    if history and history not in ("true", "false"):
        raise ValueError(MESSAGES["INVALID_VALUE_IN_HISTORY_ARG"].format(arg_history))
    return query, history


def nested_to_flat(src: dict[str, Any], key: str) -> dict[str, Any]:
    """
    Convert nested dictionary to flat by contact the keys. Also converts keys in pascal string format.

    :param src: sub-dictionary that needs to convert from nested to flat. (e.g. "foo": {"bar": "some-value"})
    :param key: main key of sub-dictionary (e.g "foo")
    :return: flat dictionary with pascal formatted keys (e.g. {"FooBar": "some-value"})
    """

    flat_dict: dict[str, str] = {}
    for sub_key, sub_value in src.items():
        pascal_key = f"{key}{sub_key[0].upper() + sub_key[1:]}"
        flat_dict[pascal_key] = sub_value

    return flat_dict


def prepare_context_dict(
    response_dict: dict[str, Any], keys_with_hierarchy: tuple = (), exclude_keys: tuple = ()
) -> dict[str, str]:
    """
    Prepare the context dictionary as per the standards.

    :param response_dict: dictionary getting from API response that contains sub-dictionaries
    :param keys_with_hierarchy: list of keys that contains sub-dictionary as its value.
    :param exclude_keys: keys need to exclude.
    :return: single level dictionary
    """
    simple_dict: dict[str, str] = {}
    for key, value in response_dict.items():
        if key in keys_with_hierarchy:
            simple_dict.update(nested_to_flat(response_dict.get(key, {}), key))
        elif key not in exclude_keys:
            simple_dict[key] = value
    return simple_dict


def get_context_for_whois_commands(domains: list[dict[str, Any]], score=0) -> tuple[list, list]:
    """
    Prepare context for whois and domain reputation commands.

    :param domains: list of domains return from response
    :param score:
    :return: command results for standard context and custom context for whois and domain reputation command
    """
    command_results: list[CommandResults] = []
    custom_context: list[dict[str, Any]] = []
    # set domain standard context
    for domain in domains:
        # set domain standard context
        if auto_detect_indicator_type(domain.get("domain", "")) == FeedIndicatorType.Domain:
            standard_context_domain = Common.Domain(
                domain=domain.get("domain", ""),
                creation_date=domain.get("registered", ""),
                updated_date=domain.get("registryUpdatedAt", ""),
                expiration_date=domain.get("expiresAt", ""),
                name_servers=domain.get("nameServers", ""),
                organization=domain.get("organization", ""),
                admin_name=domain.get("admin", {}).get("name", ""),
                admin_email=domain.get("admin", {}).get("email", ""),
                admin_phone=domain.get("admin", {}).get("telephone", ""),
                admin_country=domain.get("admin", {}).get("country", ""),
                registrar_name=domain.get("registrar", ""),
                registrant_email=domain.get("registrant", {}).get("email", ""),
                registrant_name=domain.get("registrant", {}).get("name", ""),
                registrant_phone=domain.get("registrant", {}).get("telephone", ""),
                registrant_country=domain.get("registrant", {}).get("country", ""),
                dbot_score=Common.DBotScore(
                    indicator=domain.get("domain", ""),
                    indicator_type=DBotScoreType.DOMAIN,
                    integration_name=INTEGRATION_NAME,
                    score=score,
                    reliability=demisto.params().get("integrationReliability"),
                ),
            )
            # add standard output with standard readable output
            command_results.append(
                CommandResults(
                    indicator=standard_context_domain,
                    readable_output=tableToMarkdown("", standard_context_domain.to_context().get(Common.Domain.CONTEXT_PATH)),
                )
            )

        # set custom context for whois commands
        custom_context.append(
            prepare_context_dict(
                response_dict=domain,
                keys_with_hierarchy=("registrant", "admin", "billing", "tech"),
                exclude_keys=("zone", "rawText"),
            )
        )
    return command_results, createContext(custom_context, removeNull=True)


def get_context_for_get_whois_commands(domains: list[dict[str, Any]]) -> list:
    """
    Prepare context for pt-get-whois command.

    :param domains: list of domains return from response
    :return: command results for custom context for whois command
    """
    custom_context: list[dict[str, Any]] = []
    # set domain standard context
    for domain in domains:
        custom_context.append(
            prepare_context_dict(
                response_dict=domain,
                keys_with_hierarchy=("registrant", "admin", "billing", "tech"),
                exclude_keys=("zone", "rawText"),
            )
        )
    return createContext(custom_context, removeNull=True)


def prepare_hr_cell_for_whois_info(domain_info: dict[str, Any]) -> str:
    """
    Prepare cell information for Registrant, Admin, Billing and Tech columns.

    :param domain_info: domain information as a directory
    :return: domain information as a readable format
    """
    hr_cell_info: list[str] = []
    for key, value in domain_info.items():
        hr_cell_info.append(f"**{key[0].upper() + key[1:]}:** {value}")
    return ",\n".join(hr_cell_info)


def get_human_readable_for_whois_commands(domains: list[dict[str, Any]], is_reputation_command=False):
    """
    Prepare readable output for whois commands

    :param domains: list of domains return from response
    :param is_reputation_command: true if the command is execute for reputation command else false, default is false
    :return: markdown of whois commands based on domains passed
    """
    hr_table_content: list[dict[str, Any]] = []
    table_name = "Domain(s)"
    for domain in domains:
        hr_row = {
            "Domain": domain.get("domain", ""),
            "WHOIS Server": domain.get("whoisServer", ""),
            "Registrar": domain.get("registrar", ""),
            "Contact Email": domain.get("contactEmail", ""),
            "Name Servers": ", ".join(domain.get("nameServers", [])),
            "Creation Date (GMT)": domain.get("registered", ""),
            "Expire Date (GMT)": domain.get("expiresAt", ""),
            "Updated Date (GMT)": domain.get("registryUpdatedAt", ""),
            "Last Scanned (GMT)": domain.get("lastLoadedAt", ""),
            "Registrant": prepare_hr_cell_for_whois_info(domain.get("registrant", {})),
            "Admin": prepare_hr_cell_for_whois_info(domain.get("admin", {})),
            "Billing": prepare_hr_cell_for_whois_info(domain.get("billing", {})),
            "Tech": prepare_hr_cell_for_whois_info(domain.get("tech", {})),
        }
        hr_table_content.append(hr_row)

    hr = ""
    if not is_reputation_command:
        hr += "### Total Retrieved Record(s): " + str(len(domains)) + "\n"
        table_name = "Associated Domains"
    hr += tableToMarkdown(
        name=table_name,
        t=hr_table_content,
        headers=[
            "Domain",
            "WHOIS Server",
            "Registrar",
            "Contact Email",
            "Name Servers",
            "Registrant",
            "Admin",
            "Billing",
            "Tech",
            "Creation Date (GMT)",
            "Expire Date (GMT)",
            "Updated Date (GMT)",
            "Last Scanned (GMT)",
        ],
        removeNull=True,
    )
    return hr


def get_human_readable_for_articles_commands(articles: list[dict[str, Any]]):
    """
    Prepare readable output for pt-get-articles command

    :param articles: list of articles return from response
    :return: markdown of article command based on articles passed
    """
    hr_table_content: list[dict[str, Any]] = []
    table_name = "Article(s)"
    for article in articles:
        hr_row = {
            "GUID": article.get("guid", ""),
            "Title": article.get("title", ""),
            "Summary": article.get("summary", ""),
            "Type": article.get("type", ""),
            "Tags": ", ".join(article.get("tags", [])) if article.get("tags", []) else "",
            "Categories": ", ".join(article.get("categories", [])) if article.get("categories", []) else "",
            "Article Link": article.get("link", ""),
            "Published Date (GMT)": article.get("publishedDate", ""),
        }
        hr_table_content.append(hr_row)

    hr = "### Total Retrieved Record(s): " + str(len(articles)) + "\n"
    hr += tableToMarkdown(
        name=table_name,
        t=hr_table_content,
        headers=["GUID", "Title", "Summary", "Type", "Tags", "Categories", "Article Link", "Published Date (GMT)"],
        removeNull=True,
    )
    return hr


def get_human_readable_for_data_card_command(results: dict[str, Any]) -> str:
    """
    Parse and convert the Data Card Summary in the response into human-readable markdown string.

    :type results: ``List[Dict[str, Any]]``
    :param results: Details of urls.

    :return: Human Readable string containing information of data card.
    :rtype: ``str``
    """
    hr_table_content: list[dict[str, Any]] = []
    summary = results.get("data_summary", {})
    summary = {pascalToSpace(underscoreToCamelCase(k)): v for k, v in summary.items()}

    hr_row = {
        "Name": results.get("name", ""),
        "Type": results.get("type", ""),
        "Netblock": results.get("netblock", ""),
        "Autonomous System Number": results.get("asn", ""),
        "Host Provider": results.get("hosting_provider", ""),
        "Operating System": results.get("os", ""),
        "Data Card Summary": get_data_card_summary(summary),
    }
    hr_table_content.append(hr_row)

    return tableToMarkdown(
        "Data Card Summary",
        hr_table_content,
        headers=[
            "Name",
            "Type",
            "Netblock",
            "Autonomous System Number",
            "Host Provider",
            "Operating System",
            "Data Card Summary",
        ],
        removeNull=True,
    )


def get_human_readable_for_reputation_command(results: dict[str, Any], query: str) -> str:
    """
    Parse and convert the response into human-readable markdown string.

    :type results: ``List[Dict[str, Any]]``
    :param results: Details of urls.

    :type query: ``str``
    :param query: Value of the indicator.

    :return: Human Readable string containing information.
    :rtype: ``str``
    """
    classification = results.get("classification", "")

    reputation_score = results.get("score", "")

    reputation_message = f"The reputation score for '{query}' is {reputation_score} and is classified as '{classification}'.\n"

    hr_table_content: list[dict[str, Any]] = []

    rules = results.get("rules", [])
    for rule in rules:
        link = rule.get("link")
        if link:
            name = f"[{rule.get('name', '')}]({rule.get('link')})"
        else:
            name = rule.get("name", "")
        hr_row = {"Name": name, "Description": rule.get("description", ""), "Severity": rule.get("severity", "")}
        hr_table_content.append(hr_row)
    hr_table = tableToMarkdown("Reputation Rules", hr_table_content, headers=["Name", "Description", "Severity"])

    return reputation_message + hr_table


def prepare_human_readable_dict_for_ssl(results: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """
    Preparing human-readable dictionary for ssl certificate command.

    :param results: ssl certificates details
    :return: human-readable dict
    """
    return [
        {
            "First (GMT)": epochToTimestamp(result.get("firstSeen", "")) if result.get("firstSeen") else None,
            "Last (GMT)": epochToTimestamp(result.get("lastSeen", "")) if result.get("lastSeen") else None,
            "SSL Version": result.get("sslVersion", 0),
            "Expires (GMT)": result.get("expirationDate", ""),
            "Issued (GMT)": result.get("issueDate", ""),
            "Sha1": result.get("sha1", ""),
            "Serial Number": result.get("serialNumber", ""),
            "Subject Country": result.get("subjectCountry", ""),
            "Issuer Common Name": result.get("issuerCommonName", ""),
            "Issuer Province": result.get("issuerProvince", ""),
            "Subject State/Province Name": result.get("subjectStateOrProvinceName", ""),
            "Subject Street Address": result.get("subjectStreetAddress", ""),
            "Issuer State/Province Name": result.get("issuerStateOrProvinceName", ""),
            "Issuer Country": result.get("issuerCountry", ""),
            "Subject Locality Name": result.get("subjectLocalityName", ""),
            "Subject Alternative Names": ", ".join(result.get("subjectAlternativeNames", [])),
            "Issuer Organization Unit Name": result.get("issuerOrganizationUnitName", ""),
            "Issuer Organization Name": result.get("issuerOrganizationName", ""),
            "Subject Organization Name": result.get("subjectOrganizationName", ""),
            "Issuer Locality Name": result.get("issuerLocalityName", ""),
            "Subject Common Name": result.get("subjectCommonName", ""),
            "Subject Province": result.get("subjectProvince", ""),
            "Subject Organization Unit Name": result.get("subjectOrganizationUnitName", ""),
            "Issuer Street Address": result.get("issuerStreetAddress", ""),
        }
        for result in results
    ]


def prepare_human_readable_dict_for_pdns(results: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """
    Prepare human-readable dictionary for passive DNS command.

    :param results: passive DNS details.
    :return: human-readable dict
    """
    return [
        {
            "First (GMT)": result.get("firstSeen", ""),
            "Last (GMT)": result.get("lastSeen", ""),
            "Source": ", ".join(result.get("source", [])),
            "Value": result.get("value", ""),
            "Collected (GMT)": result.get("collected", ""),
            "Record Type": result.get("recordType", ""),
            "Resolve": result.get("resolve", ""),
            "Resolve Type": result.get("resolveType", ""),
            "Record Hash": result.get("recordHash", ""),
        }
        for result in results
    ]


def get_ssl_cert_search_hr(results: list[dict[str, Any]]) -> str:
    """
    Retrieved information of ssl certificates and convert it into human-readable markdown string.

    :param results: ssl certificates details
    :return: human-readable string
    """
    ssl_cert_search_hr: list[dict[str, Any]] = prepare_human_readable_dict_for_ssl(results)

    hr = "### Total Retrieved Record(s): " + str(len(results)) + "\n"
    hr += tableToMarkdown(
        "SSL certificate(s)",
        ssl_cert_search_hr,
        [
            "Sha1",
            "Serial Number",
            "Issued (GMT)",
            "Expires (GMT)",
            "SSL Version",
            "First (GMT)",
            "Last (GMT)",
            "Issuer Common Name",
            "Subject Common Name",
            "Subject Alternative Names",
            "Issuer Organization Name",
            "Subject Organization Name",
            "Issuer Locality Name",
            "Subject Locality Name",
            "Issuer State/Province Name",
            "Subject State/Province Name",
            "Issuer Country",
            "Subject Country",
            "Issuer Street Address",
            "Subject Street Address",
            "Issuer Organization Unit Name",
            "Subject Organization Unit Name",
        ],
        removeNull=True,
    )

    return hr


def get_pdns_details_hr(results: list[dict[str, Any]], total_record: int) -> str:
    """
    Retrieved information of passive DNS and convert it into human-readable markdown string.

    :param results: passive DNS details
    :param total_record: number of record retrieved from response
    :return: human-readable string
    """
    pdns_details_hr: list[dict[str, Any]] = prepare_human_readable_dict_for_pdns(results)

    table_headers = [
        "Resolve",
        "Resolve Type",
        "Record Type",
        "Collected (GMT)",
        "First (GMT)",
        "Last (GMT)",
        "Source",
        "Record Hash",
    ]
    hr = "### Total Retrieved Record(s): " + str(total_record) + "\n"
    hr += tableToMarkdown("PDNS detail(s)", pdns_details_hr, table_headers, removeNull=True)
    return hr


def create_pdns_standard_context(results: list[dict[str, Any]]) -> list[CommandResults]:
    """
    Preparing standard context and dbotScore for pdns command.
    here, standard context includes ip and domain model.

    :param results: pdns details from response
    :return: list of CommandResults of standard context
    """
    standard_results: list[CommandResults] = []
    for result in results:
        resolve_type = result.get("resolveType", "")
        resolve = result.get("resolve", "")

        if resolve_type == "domain":
            dbot_score = Common.DBotScore(resolve, resolve_type, INTEGRATION_NAME, Common.DBotScore.NONE)
            if auto_detect_indicator_type(resolve) == FeedIndicatorType.Domain:
                domain_ioc = Common.Domain(resolve, dbot_score)
                # add standard output with standard readable output
                standard_results.append(
                    CommandResults(
                        indicator=domain_ioc,
                        readable_output=tableToMarkdown("", domain_ioc.to_context().get(Common.Domain.CONTEXT_PATH)),
                    )
                )
        elif resolve_type == "ip":
            dbot_score = Common.DBotScore(resolve, resolve_type, INTEGRATION_NAME, Common.DBotScore.NONE)
            if auto_detect_indicator_type(resolve) == FeedIndicatorType.IP:
                ip_ioc = Common.IP(resolve, dbot_score)
                # add standard output with standard readable output
                standard_results.append(
                    CommandResults(
                        indicator=ip_ioc, readable_output=tableToMarkdown("", ip_ioc.to_context().get(Common.IP.CONTEXT_PATH))
                    )
                )

    return standard_results


def get_services_hr(total_records: int, results: list) -> str:
    services_hr = []
    for result in results:
        current_services = result.get("currentServices", [])
        labels = []
        for services in current_services:
            if services.get("label"):
                labels.append(services.get("label"))

        services_hr.append(
            {
                "Port Number": result.get("portNumber"),
                "Protocol": result.get("protocol"),
                "Status": result.get("status"),
                "Current Service Labels": ", ".join(labels),
                "First Seen Date (GMT)": result.get("firstSeen"),
                "Last Seen Date (GMT)": result.get("lastSeen"),
                "Last Scanned Date (GMT)": result.get("lastScan"),
            }
        )

    table_output = tableToMarkdown(
        "Services",
        services_hr,
        headers=[
            "Port Number",
            "Protocol",
            "Status",
            "Current Service Labels",
            "First Seen Date (GMT)",
            "Last Seen Date (GMT)",
            "Last Scanned Date (GMT)",
        ],
        removeNull=True,
    )

    return f"### Total Retrieved Record(s) {total_records} \n {table_output}"


def validate_get_cookies_arguments(args: dict[str, Any]) -> None:
    """
    Validate arguments for get cookies command, raise ValueError on invalid arguments.

    :param args: stripped arguments provided by user
    """

    # check if page is a valid whole number
    try:
        if int(args.get("page", "0")) < 0:
            raise ValueError
    except ValueError:
        raise ValueError(MESSAGES["INVALID_WHOLE_NUMBER"].format("page"))

    # check if the single-select values of arguments are valid or not
    valid_values = {
        "search_by": [
            "get addresses by cookie domain",
            "get addresses by cookie name",
            "get hosts by cookie domain",
            "get hosts by cookie name",
        ],
        "order": ["desc", "asc"],
        "sort": ["first seen", "last seen"],
    }
    for argument_name, valid_value in valid_values.items():
        if args.get(argument_name) not in valid_value:
            raise ValueError(MESSAGES["INVALID_SINGLE_SELECT"].format(argument_name, ",".join(valid_value)))

    # validate query param based on search_by
    if "domain" in args["search_by"]:
        # The domain should only contain the characters a-z, A-Z, hyphen (-) and fullstops (.).
        if not args["query"] or re.search("[^a-zA-Z0-9.-]", args["query"]):
            raise ValueError(MESSAGES["INVALID_QUERY_COOKIE_DOMAIN"])
    else:
        """ Valid cookie name can be any US-ASCII characters,
            except control characters, spaces, separator character or tabs."""
        if not args["query"] or re.search(r'[?^()<>@,;:/="\[\]{}\\\t\n\s]', args["query"]):
            raise ValueError(MESSAGES["INVALID_QUERY_COOKIE_NAME"])


def get_cookies_hr(results: list[dict[str, Any]], hostname_header: str, total_records: int):
    """
    Retrieved information of cookies and convert it into human-readable markdown string.

    :param results: cookie details
    :param hostname_header: The header for the hostname column, should be either 'Hostname' or 'IP Address'
    :param total_records: Total number of available records
    :return: human-readable string
    """
    hr_results = [
        {
            hostname_header: result.get("hostname", ""),
            "Cookie Name": result.get("cookieName", ""),
            "Cookie Domain": result.get("cookieDomain", ""),
            "First Seen Date (GMT)": result.get("firstSeen", ""),
            "Last Seen Date (GMT)": result.get("lastSeen", ""),
        }
        for result in results
    ]

    table_headers = [hostname_header, "Cookie Name", "Cookie Domain", "First Seen Date (GMT)", "Last Seen Date (GMT)"]

    hr = "### Total Record(s): " + str(total_records) + "\n"
    hr += "### Total Retrieved Record(s): " + str(len(hr_results)) + "\n"
    hr += tableToMarkdown("Cookies", hr_results, table_headers, removeNull=True)
    return hr


def calculate_dbot_score(classification: str) -> int:
    """
    Calculate te dbot score according to the classification provided
    :param classification: The classification received from he api response
    :return: Dbot Score
    """
    if classification == "UNKNOWN":
        score = Common.DBotScore.NONE  # unknown
    elif classification == "MALICIOUS":
        score = Common.DBotScore.BAD  # bad
    elif classification == "SUSPICIOUS":
        score = Common.DBotScore.SUSPICIOUS  # suspicious
    else:
        score = Common.DBotScore.GOOD  # good
    return score


def get_standard_context(score: int, ip: str) -> Common.IP:
    """
    Prepare standard context for ip reputation command
    :param score: The Dbot score
    :param ip: IP Address
    :return: IP indicator object
    """
    dbot_score = Common.DBotScore(
        indicator=ip,
        integration_name="PassiveTotal",
        indicator_type=DBotScoreType.IP,
        score=score,
        reliability=demisto.params().get("integrationReliability"),
    )
    return Common.IP(ip=ip, dbot_score=dbot_score)


def get_data_card_summary(summary: dict) -> str:
    """
    Prepare data card summary
    :param summary: data card summary response
    :return: human readable string
    """
    data_card_summary = ""
    for key, value in summary.items():
        if value.get("count") == 0:
            data_card_summary += "{}: {}, ".format(key, value.get("count"))
        else:
            data_card_summary += "{}: [{}]({}), ".format(key, value.get("count"), value.get("link"))

    return data_card_summary[:-2]


def validate_list_intel_profile_args(args: dict[str, str]) -> tuple[dict[str, Any], int]:
    """
    Validate arguments for pt-list-intel-profiles, raise ValueError on invalid arguments.

    :param args: The command arguments provided by the user.
    :return: Parameters to send in request

    """
    params: dict[str, Any] = {}
    query = args.get("query", "")
    if query:
        params["query"] = query

    profile_type = args.get("type", "").lower()
    if profile_type:
        if profile_type not in PROFILE_TYPE:
            raise ValueError(MESSAGES["INVALID_PROFILE_TYPE"].format(profile_type))
        params["type"] = profile_type

    indicator_value = args.get("indicator_value", "")
    if indicator_value:
        params["query"] = indicator_value

    source = args.get("source", "").lower()
    if source:
        if not indicator_value:
            raise ValueError(MESSAGES["REQUIRED_INDICATOR_VALUE"])
        if source not in SOURCE:
            raise ValueError(MESSAGES["INVALID_SOURCE"].format(source))
        params["sources"] = source

    category = args.get("category", "").lower()
    if category:
        if not indicator_value:
            raise ValueError(MESSAGES["REQUIRED_INDICATOR_VALUE"])
        params["categories"] = category

    page_size = validate_page_size_args(args)

    return params, page_size


def get_human_readable_for_intel_profile_command(results: list[dict[str, Any]]) -> str:
    """
    Parse and convert the response into human-readable markdown string.

    :param results: Details of profiles.
    :return: Human Readable string containing information.
    """
    hr_list = []
    for profile in results:
        hr_record = {
            "ID": profile.get("id", ""),
            "Title": profile.get("title", ""),
            "Aliases": ", ".join(f"{i}" for i in profile.get("aliases", [])),
            "Public Indicators": profile.get("osintIndicatorsCount", ""),
            "RiskIQ Indicators": profile.get("riskIqIndicatorsCount", ""),
        }

        hr_list.append(hr_record)

    return tableToMarkdown(
        "Profile(s)", hr_list, ["ID", "Title", "Aliases", "Public Indicators", "RiskIQ Indicators"], removeNull=True
    )


def validate_page_and_size_args(args: dict[str, str]) -> dict:
    """
    Validate page and size arguments for all commands, raise ValueError on invalid arguments.
    :param args: The command arguments provided by the user.
    :return: Parameters to send in request
    """

    params: dict[str, Any] = {}

    page_size = arg_to_number(args.get("page_size", DEFAULT_SIZE))

    if page_size is not None:
        if page_size < 1 or page_size > 2147483647:
            raise ValueError(MESSAGES["INVALID_PAGE_SIZE"].format(page_size))
    else:
        page_size = int(DEFAULT_SIZE)
    params["size"] = page_size

    page_number = arg_to_number(args.get("page_number", DEFAULT_PAGE_NUMBER))
    if page_number:
        if page_number < 0 or page_number > 2147483647:
            raise ValueError(MESSAGES["INVALID_PAGE_NUMBER"].format(page_number))
    else:
        page_number = int(DEFAULT_PAGE_NUMBER)
    params["page"] = page_number

    return params


def validate_list_intel_profile_indicators_args(args: dict[str, Any]) -> tuple[str, dict]:
    """
    Validate arguments of list_intel_profile_indicators commands, raise ValueError on invalid arguments.
    :param args: The command arguments provided by the user.
    :return: Parameters to send in request
    """
    profile_id = args.get("id", "")
    if not profile_id:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("id"))

    params = validate_page_and_size_args(args)

    indicator_type = args.get("type", "").lower()
    if indicator_type:
        if indicator_type not in INDICATOR_TYPE:
            raise ValueError(MESSAGES["INVALID_INDICATOR_TYPE"].format(indicator_type))
        params["types"] = indicator_type

    indicator_value = args.get("indicator_value", "")
    if indicator_value:
        params["query"] = indicator_value

    source = args.get("source", "").lower()
    if source:
        if source not in SOURCE:
            raise ValueError(MESSAGES["INVALID_SOURCE"].format(source))
        params["sources"] = source

    category = args.get("category", "").lower()
    if category:
        params["categories"] = category

    return profile_id, params


def get_human_readable_for_intel_profile_indicator_command(indicators: dict[str, Any]) -> str:
    """
    Parse and convert the intel profile indicator list into human-readable markdown string.
    :param indicators: Details of indicators.
    :return: Human Readable string containing information of intel profile indicators.
    """
    hr_table_content: list[dict[str, Any]] = []
    total_records = indicators.get("totalCount", 0)

    for indicator in indicators.get("results", []):
        hr_row = {
            "ID": indicator.get("id", ""),
            "Artifact Value": indicator.get("value", ""),
            "Type": indicator.get("type", ""),
            "First Seen (GMT)": indicator.get("firstSeen", ""),
            "Last Seen (GMT)": indicator.get("lastSeen", ""),
            "Source": "OSINT" if indicator.get("osint", True) else "RiskIQ",
        }
        hr_table_content.append(hr_row)

    hr = f"### Total Retrieved Indicator(s) {total_records}\n"
    hr += tableToMarkdown(
        "Indicator(s)",
        hr_table_content,
        headers=["ID", "Artifact Value", "Type", "First Seen (GMT)", "Last Seen (GMT)", "Source"],
        removeNull=True,
    )
    return hr


def validate_list_my_asi_insights_args(args: dict[str, Any]) -> tuple[str, int]:
    """
    Validate arguments of pt-list-my-attack-surface-insights, pt-list-third-party-asi-insights command, raise ValueError
    on invalid arguments.

    :param args: The command arguments provided by the user.
    :return: Parameters to send in request
    """

    priority = args.get("priority", "").lower()

    if priority:
        if priority not in PRIORITY:
            raise ValueError(MESSAGES["INVALID_PRIORITY_LEVEL"].format(priority))
    else:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("priority"))

    page_size = validate_page_size_args(args)

    return priority, page_size


def prepare_context_for_my_asi_insights(response, priority_level) -> dict[str, Any]:
    """
    Prepare context data for pt-list-my-attack-surface-insights, pt-list-third-party-asi-insights command
    :param response: API response.
    :param priority_level: The priority level provided by the user.
    :return: context data
    """
    context_data = {"priorityLevel": priority_level}

    insights = response.get("insights", [])

    for insight in insights:
        link = insight.get("link", "")
        if link:
            # retrieve segment_by and insight_id from the link received from api response
            link_split = link.split("?")
            insight["insightId"] = link_split[0].split("/")[-1]
            query_parameters = parse.parse_qs(link_split[1])
            insight["segmentBy"] = query_parameters.get("segmentBy", "")[0]

    context_data["insight"] = insights

    return context_data


def get_human_readable_for_my_asi_insights_command(
    results: dict[str, Any], priority: str, active_insights: int, total_insights: int, total_observations: int
) -> str:
    """
    Parse and convert the asi insight list into human-readable markdown string.
    :param results: Details of insights.
    :param priority: Priority of insights.
    :param active_insights: Total active insights
    :param total_insights: Total number of insights
    :param total_observations: Total observations
    :return: Human Readable string containing information.
    """
    hr_table_content: list[dict[str, Any]] = []

    insights = results.get("insight", {})
    for insight in insights:
        hr_row = {
            "Name": insight.get("name", ""),
            "Description": insight.get("description", ""),
            "Observations": insight.get("observationCount", ""),
            "Insight ID": insight.get("insightId", ""),
            "Segment By": insight.get("segmentBy", ""),
        }
        hr_table_content.append(hr_row)

    # sort on the basis of observation count
    sorted_hr_table = sorted(hr_table_content, key=lambda item: item["Observations"], reverse=True)

    table_heading = (
        f"{priority.capitalize()} Severity Insights\n{active_insights} Active of {total_insights} "
        f"Insights - {total_observations} Observations\n"
    )
    hr = tableToMarkdown(
        table_heading,
        sorted_hr_table,
        headers=["Name", "Description", "Observations", "Insight ID", "Segment By"],
        removeNull=True,
    )
    return hr


def get_human_readable_for_attack_surface_command(response: list[dict[str, Any]]) -> str:
    """
    Parse and convert the attack surface list into human-readable markdown string.
    :param response: Details of attack surface.
    :return: Human Readable string containing information of attack surface.
    """
    hr_table_content: list[dict[str, Any]] = []

    for result in response:
        hr_row = {
            "ID": result.get("id", ""),
            "Name": f"[{result.get('name', '')}]({UI_LINK}{result.get('id', '')})",
            "High Severity": f"{result.get('priorities', {}).get('high', {}).get('observationCount', 0)} observations",
            "Medium Severity": f"{result.get('priorities', {}).get('medium', {}).get('observationCount', 0)} observations",
            "Low Severity": f"{result.get('priorities', {}).get('low', {}).get('observationCount', 0)} observations",
        }
        hr_table_content.append(hr_row)

    return tableToMarkdown(
        "Attack Surface(s)",
        hr_table_content,
        headers=["ID", "Name", "High Severity", "Medium Severity", "Low Severity"],
        removeNull=True,
    )


def validate_list_my_asi_assets_args(args: dict[str, Any]) -> tuple[str, dict]:
    """
    Validate arguments of list_my_asi_assets_command commands, raise ValueError on invalid arguments.
    :param args: The command arguments provided by the user.
    :return: Parameters to send in request
    """
    insight_id = args.get("id", "")
    if not insight_id:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("id"))

    segment_by = args.get("segment_by", "")
    if not segment_by:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("segment_by"))

    params = validate_page_and_size_args(args)
    params["segmentBy"] = segment_by

    return insight_id, params


def get_human_readable_for_my_asi_assets_command(results: dict[str, Any]) -> str:
    """
    Parse and convert the asi asset list into human-readable markdown string.
    :param results: Details of assets.
    :return: Human Readable string containing information.
    """
    hr_table_content: list[dict[str, Any]] = []
    assets = results.get("assets", [])
    for asset in assets:
        hr_row = {
            "Name": asset.get("name", ""),
            "Type": asset.get("type", ""),
            "First Seen (GMT)": asset.get("firstSeen", ""),
            "Last Seen (GMT)": asset.get("lastSeen", ""),
        }
        hr_table_content.append(hr_row)

    hr = tableToMarkdown(
        "Asset(s)", hr_table_content, headers=["Name", "Type", "First Seen (GMT)", "Last Seen (GMT)"], removeNull=True
    )
    return hr


def get_human_readable_for_my_asi_vulnerable_components_command(results: list[dict[str, Any]]) -> str:
    """
    Parse and convert the asi vulnerable component list into human-readable markdown string.
    :param results: Details of vulnerable component.
    :return: Human Readable string containing information.
    """
    hr_table_content: list[dict[str, Any]] = []
    for component in results:
        hr_row = {
            "Name": component.get("name", ""),
            "Type": component.get("type", ""),
            "Severity": component.get("severity", ""),
            "Asset Count": component.get("count", ""),
        }
        hr_table_content.append(hr_row)

    hr = tableToMarkdown(
        "Vulnerable Component(s)", hr_table_content, headers=["Name", "Type", "Severity", "Asset Count"], removeNull=True
    )
    return hr


def get_human_readable_for_my_asi_vulnerabilities_command(results: list[dict[str, Any]]) -> str:
    """
    Parse and convert the asi vulnerabilities list into human-readable markdown string.
    :param results: Details of vulnerability.
    :return: Human Readable string containing information.
    """
    hr_table_content: list[dict[str, Any]] = []
    for component in results:
        hr_row = {
            "CVE ID": component.get("cveId", ""),
            "CWE ID": f'{", ".join("{}".format(i.get("cweId")) for i in component.get("cwes", []))} ',
            "RiskIQ Priority Score": component.get("priorityScore", ""),
            "Asset Count": component.get("observationCount", ""),
        }
        hr_table_content.append(hr_row)

    hr = tableToMarkdown(
        "Vulnerabilities", hr_table_content, headers=["CVE ID", "CWE ID", "RiskIQ Priority Score", "Asset Count"], removeNull=True
    )
    return hr


def get_human_readable_for_my_asi_observations_command(results: dict[str, Any]) -> str:
    """
    Parse and convert the asi observations list into human-readable markdown string.
    :param results: Details of observations.
    :return: Human Readable string containing information.
    """
    hr_table_content: list[dict[str, Any]] = []
    assets = results.get("assets", [])
    for asset in assets:
        hr_row = {
            "Name": asset.get("name", ""),
            "Type": asset.get("type", ""),
            "First Seen (GMT)": asset.get("firstSeen", ""),
            "Last Seen (GMT)": asset.get("lastSeen", ""),
        }
        hr_table_content.append(hr_row)

    hr = tableToMarkdown(
        "Observation(s)", hr_table_content, headers=["Name", "Type", "First Seen (GMT)", "Last Seen (GMT)"], removeNull=True
    )
    return hr


def validate_page_size_args(args: dict[str, str]) -> int:
    """
    Validate page_size argument for all command which does not supports pagination, raise ValueError on invalid
    arguments.
    :param args: The command arguments provided by the user.
    :return: Validated page_size argument
    """

    page_size = arg_to_number(args.get("page_size", DEFAULT_SIZE))

    if page_size is not None:
        if page_size < 1 or page_size > 1000:
            raise ValueError(MESSAGES["NOT_VALID_PAGE_SIZE"].format(page_size))
    else:
        page_size = int(DEFAULT_SIZE)

    return page_size


""" REQUESTS FUNCTIONS """


def test_function(client: Client) -> str:
    """
    Performs test connectivity by valid http response

    :param client: client object which is used to get response from api
    :return: raise ValueError if any error occurred during connection
    """
    client.http_request(method="GET", url_suffix=URL_SUFFIX["TEST_MODULE"])

    return "ok"


@logger
def get_components_command(client: Client, args: dict[str, Any]) -> str | list[CommandResults]:
    """
    Retrieves the host attribute components for a domain or IP address.

    :param client: Client object
    :param args: The command arguments provided by user
    :return: Standard command result or no records found message
    """
    # Trim the arguments
    for argument in args:
        args[argument] = args[argument].strip()

    # Retrieve arguments and prepare query data
    params = get_common_arguments(args)

    # http call
    resp = client.http_request(method="GET", url_suffix=URL_SUFFIX["GET_COMPONENTS"], params=params)

    total_records = resp.get("totalRecords", 0)
    if total_records == 0:
        return MESSAGES["NO_RECORDS_FOUND"].format("component(s)")

    results = resp.get("results", [])

    # Creating entry context
    command_results, custom_ec = get_host_attribute_context_data(results)

    # Creating human-readable
    hr = get_components_hr(results)

    command_results.insert(
        0,
        CommandResults(
            outputs_prefix="PassiveTotal.Component",
            outputs_key_field="",
            outputs=custom_ec,
            readable_output=hr,
            raw_response=resp,
        ),
    )

    return command_results


@logger
def pt_whois_search_command(client_obj: Client, args: dict[str, Any]) -> list[CommandResults] | str:
    """
    Gets WHOIS information records based on field matching queries

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-whois-search
    :return: command output
    """
    # Retrieve arguments
    query, field = get_valid_whois_search_arguments(args)
    params = {"query": query, "field": field}

    # http call
    response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["WHOIS_SEARCH"], params=params)

    domains = response.get("results", [])
    if len(domains) <= 0:
        return MESSAGES["NO_RECORDS_FOUND"].format("domain information")

    # Creating entry context
    command_results, custom_ec = get_context_for_whois_commands(domains)

    # Creating human-readable
    hr = get_human_readable_for_whois_commands(domains)

    command_results.insert(
        0,
        CommandResults(
            outputs_prefix="PassiveTotal.WHOIS",
            outputs_key_field=["domain", "lastLoadedAt"],
            outputs=custom_ec,
            readable_output=hr,
            raw_response=response,
        ),
    )

    return command_results


@logger
def get_trackers_command(client: Client, args: dict[str, Any]) -> str | list[CommandResults]:
    """
    Retrieves the host attribute trackers for a domain or IP address.

    :param client: Client object
    :param args: The command arguments provided by user
    :return: Standard command result or no records found message
    """
    # Trim the arguments
    for argument in args:
        args[argument] = args[argument].strip()

    # Retrieve arguments and prepare query data
    params = get_common_arguments(args)

    # http call
    resp = client.http_request(method="GET", url_suffix=URL_SUFFIX["GET_TRACKERS"], params=params)

    total_records = resp.get("totalRecords", 0)
    if total_records == 0:
        return MESSAGES["NO_RECORDS_FOUND"].format("tracker(s)")

    results = resp.get("results", [])

    # Creating entry context
    command_results, custom_ec = get_host_attribute_context_data(results)

    # Creating human-readable
    hr = get_trackers_hr(results)

    command_results.insert(
        0,
        CommandResults(
            outputs_prefix="PassiveTotal.Tracker",
            outputs_key_field="",
            outputs=custom_ec,
            readable_output=hr,
            raw_response=resp,
        ),
    )

    return command_results


@logger
def ssl_cert_search_command(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Retrieve SSL certificates based on various field and its value.

    :param client: client object.
    :param args: Demisto argument(s) provided by the user.
    :return: standard command result
    """
    # Retrieve arguments
    field: str = args.get("field", "")
    query: str = args.get("query", "")

    # Building url
    url_suffix: str = URL_SUFFIX["SSL_CERT_SEARCH"]

    # Prepare query data
    request_data: dict[str, Any] = {"field": field, "query": query}

    # http call
    resp: dict[str, Any] = client.http_request("GET", url_suffix, request_data)

    results: list[dict[str, Any]] = resp.get("results", [])
    if len(results) <= 0:
        return MESSAGES["NO_RECORDS_FOUND"].format("SSL certificate(s)")

    # Creating entry context
    custom_ec = createContext(results, removeNull=True)

    # Creating human-readable
    hr = get_ssl_cert_search_hr(results)

    return CommandResults(
        outputs_prefix="PassiveTotal.SSL", outputs_key_field="sha1", outputs=custom_ec, readable_output=hr, raw_response=resp
    )


@logger
def get_pdns_details_command(client: Client, args: dict[str, Any]) -> str | list[CommandResults]:
    """
    Retrieve passive DNS details based on ip or host.

    :param client: client object.
    :param args: Demisto argument(s) provided by the user.
    :return: standard command result
    """
    # Retrieve arguments
    params = get_common_arguments(args)
    params["timeout"] = args.get("timeout", 7)

    # http call
    resp = client.http_request("GET", url_suffix=URL_SUFFIX["GET_PDNS_DETAILS"], params=params)
    total_record = resp.get("totalRecords", 0)
    if total_record <= 0:
        return MESSAGES["NO_RECORDS_FOUND"].format("PDNS Record(s)")

    # Creating entry context
    results = resp.get("results", [])
    custom_ec = createContext(results, removeNull=True)
    command_results = create_pdns_standard_context(results)

    # Creating human-readable
    hr = get_pdns_details_hr(results, total_record)

    # Building custom output path
    output_path = "PassiveTotal.PDNS(val.{0} == obj.{0} && val.{1} == obj.{1} && val.{2} == obj.{2})".format(
        "resolve", "recordType", "resolveType"
    )

    command_results.insert(
        0,
        CommandResults(
            outputs_prefix=output_path, outputs_key_field="", outputs=custom_ec, readable_output=hr, raw_response=resp
        ),
    )

    return command_results


@logger
def get_host_pairs_command(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Retrieves the host attribute pairs related to a domain or IP address.

    :param client: Client object
    :param args: The command arguments provided by user
    :return: Standard command result or no records found message
    """
    # Trim the arguments
    for argument in args:
        args[argument] = args[argument].strip()

    # Retrieve arguments and prepare query data
    params = get_common_arguments(args)

    if args.get("direction") not in VALID_DIRECTION_FOR_HOST_PAIRS:
        raise ValueError(MESSAGES["INVALID_DIRECTION_VALUE"])
    params["direction"] = args.get("direction")

    # http call
    resp = client.http_request(method="GET", url_suffix=URL_SUFFIX["GET_HOST_PAIRS"], params=params)

    total_records = resp.get("totalRecords", 0)
    if total_records == 0:
        return MESSAGES["NO_RECORDS_FOUND"].format("host pair(s)")

    results = resp.get("results", [])

    # Creating entry context
    custom_ec = createContext(data=results, removeNull=True)

    # Creating human-readable
    hr = get_host_pairs_hr(results)

    return CommandResults(
        outputs_prefix="PassiveTotal.HostPair", outputs_key_field="", outputs=custom_ec, readable_output=hr, raw_response=resp
    )


@logger
def domain_reputation_command(client_obj: Client, args: dict[str, Any]) -> str | list[CommandResults]:
    """
    Reputation command for domain.

    :param client_obj: Client object
    :param args: The command arguments provided by user
    :return: Standard command result or no records found message
    """
    # Retrieve arguments
    domains = argToList(args.get("domain", ""))

    # argument validation
    if len(domains) == 0:
        raise ValueError("domain(s) not specified")

    command_results: list[CommandResults] = []
    custom_domain_context: list[dict[str, Any]] = []
    domain_responses: list[dict[str, Any]] = []
    custom_reputation_response = []
    for domain in domains:
        # argument validation
        if domain.strip() == "":
            raise ValueError(MESSAGES["EMPTY_DOMAIN_ARGUMENT"])

        params = {"query": domain, "field": "domain"}

        # http call
        response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["WHOIS_SEARCH"], params=params)
        reputation_response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["GET_REPUTATION"], params=params)
        hr_response = get_human_readable_for_reputation_command(reputation_response, domain)
        custom_reputation_response.append(hr_response)
        custom_reputation_context = remove_empty_elements(reputation_response)

        domains_response = response.get("results", [])
        if len(domains_response) <= 0:
            continue

        classification = reputation_response.get("classification", "")
        dbot_score = calculate_dbot_score(classification)
        # Creating entry context
        standard_results, custom_context = get_context_for_whois_commands(domains_response, dbot_score)

        # Preparing context data
        command_results.extend(standard_results)
        custom_context.append(custom_reputation_context)
        final_context = {k: v for x in custom_context for k, v in x.items()}
        custom_domain_context.append(final_context)

        domain_responses += domains_response

    # Creating human-readable
    domain_standard_hr = get_human_readable_for_whois_commands(domain_responses, is_reputation_command=True)

    final_hr = domain_standard_hr
    for x in custom_reputation_response:
        final_hr += x

    command_results.insert(
        0,
        CommandResults(
            readable_output=final_hr,
            outputs_prefix="PassiveTotal.Domain",
            outputs_key_field="domain",
            outputs=custom_domain_context,
        ),
    )

    return command_results


@logger
def get_services_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the services for the specified IP address.

    :param client: Client object.
    :param args: The command arguments provided by user.
    :return: Standard command result or no records found message.
    """

    # Trim the argument and remove ' quotes surrounding ip
    ip = args["ip"].strip().strip("'")

    # Checking whether ip is valid or not
    if not is_ip_valid(ip, accept_v6_ips=True):
        raise ValueError(MESSAGES["INVALID_IP"])

    # http call
    resp = client.http_request(method="GET", url_suffix=URL_SUFFIX["GET_SERVICES"], params={"query": ip})

    total_records = resp.get("totalRecords", 0)

    if total_records == 0:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("services"))

    results = [{**response, "ip": ip} for response in resp.get("results", [])]

    # Creating context
    context_output = remove_empty_elements(results)

    # Creating human-readable
    hr = get_services_hr(total_records, results)

    return CommandResults(
        outputs_prefix="PassiveTotal.Service",
        outputs_key_field=["ip", "portNumber"],
        outputs=context_output,
        readable_output=hr,
        raw_response=resp,
    )


@logger
def pt_get_whois_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Gets WHOIS information records based on queries

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-get-whois
    :return: command output
    """
    # Retrieve arguments
    query, history = get_valid_get_whois_arguments(args)
    params = {"query": query, "history": history}

    # http call
    response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["WHOIS_GET"], params=params)

    domains = response.get("results", [])
    if len(domains) <= 0 and "domain" in response:
        domains = [response]

    if len(domains) <= 0:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("domain information"))

    # Creating entry context
    custom_context = get_context_for_get_whois_commands(domains)

    # Creating human-readable
    hr = get_human_readable_for_whois_commands(domains)

    return CommandResults(
        outputs_prefix="PassiveTotal.WHOIS",
        outputs_key_field=["domain", "lastLoadedAt"],
        outputs=custom_context,
        readable_output=hr,
        raw_response=response,
    )


@logger
def get_cookies_command(client: Client, args: dict[str, str]) -> str | CommandResults:
    """
    Retrieve cookies with hostnames or addresses based on cookie name or cookie domain.

    :param client: Client object
    :param args: The command arguments provided by user

    :return: Standard command result or no records found message
    """

    default_values = {"page": "0", "sort": "last seen", "order": "desc"}
    # Trim the arguments and fill empty optional args with default values
    for argument in args:
        args[argument] = args[argument].strip()
        if not args[argument] and argument in list(default_values.keys()):
            args[argument] = default_values[argument]

    # Validate arguments
    validate_get_cookies_arguments(args)

    # Prepare params for http request
    params = {
        "page": args.get("page"),
        "sort": "lastSeen" if args.get("sort") == "last seen" else "firstSeen",
        "order": args.get("order"),
    }

    # Prepare the url suffix as per search_by and query argument
    url_suffix = URL_SUFFIX["_".join(args["search_by"].upper().split())].format(args["query"])

    # Make the http request
    resp = client.http_request(method="GET", url_suffix=url_suffix, params=params)

    # Get results from response
    results = resp.get("results", [])

    # Get total records from response
    total_records = resp.get("totalRecords", 0)
    # return standard no records found message on empty results
    if len(results) <= 0:
        return "Total Record(s): " + str(total_records) + "\n" + MESSAGES["NO_RECORDS_FOUND"].format("cookies")

    # creating entry context
    entry_context = createContext(results, removeNull=True)

    # create human readable by providing it results and the first column header name
    human_readable = get_cookies_hr(results, "Hostname" if "hosts" in url_suffix else "IP Address", total_records)

    # return CommandResults objects
    return CommandResults(
        outputs_prefix="PassiveTotal.Cookie",
        outputs_key_field=["hostname", "cookieName", "cookieDomain"],
        outputs=entry_context,
        readable_output=human_readable,
        raw_response=resp,
    )


@logger
def pt_get_articles_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Gets an article information based on queries

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-get-articles
    :return: command output
    """
    # Retrieve arguments
    query = args.get("query", "").strip()
    article_type = args.get("type", "").strip()
    if query == "":
        raise ValueError(MESSAGES["EMPTY_GET_WHOIS_ARGUMENT"])

    params = {"query": query}

    if article_type != "":
        params["type"] = article_type

    # http call
    response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["GET_ARTICLES"], params=params)

    articles = response.get("articles", [])

    if not articles or len(articles) <= 0:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("articles"))

    # Creating entry context
    custom_ec_for_articles = remove_empty_elements(articles)

    # Creating human-readable
    hr = get_human_readable_for_articles_commands(articles)

    return CommandResults(
        outputs_prefix="PassiveTotal.Article",
        outputs_key_field="guid",
        outputs=custom_ec_for_articles,
        readable_output=hr,
        raw_response=response,
    )


@logger
def get_data_card_summary_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves a summary data card associated with the given query.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-data-card-get
    :return: command output
    """
    # Retrieve arguments
    query = args.get("query", "").strip()
    if not query:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("query"))

    params = {"query": query}

    # http call
    response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["GET_DATA_CARD_SUMMARY"], params=params)
    context_data = remove_empty_elements(response)
    hr_response = get_human_readable_for_data_card_command(response)

    return CommandResults(
        outputs_prefix="PassiveTotal.DataCard",
        outputs_key_field="name",
        outputs=context_data,
        readable_output=hr_response,
        raw_response=response,
    )


@logger
def get_reputation_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Gets reputation for a given domain, host or IP.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-reputation-get
    :return: command output
    """
    # Retrieve arguments
    query = args.get("query", "").strip()

    if not query:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("query"))

    params = {"query": query}

    # http call
    response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["GET_REPUTATION"], params=params)
    context_data = remove_empty_elements(response)
    context_data["query"] = query
    hr_response = get_human_readable_for_reputation_command(response, query)

    return CommandResults(
        outputs_prefix="PassiveTotal.Reputation",
        outputs_key_field="query",
        outputs=context_data,
        readable_output=hr_response,
        raw_response=response,
    )


@logger
def ip_reputation_command(client_obj: Client, args: dict[str, Any]) -> list[CommandResults]:
    """
    Reputation command for IP.

    :param client_obj: Client object
    :param args: The command arguments provided by user
    :return: Standard command result
    """
    # Retrieve arguments
    ips = [x.strip() for x in argToList(args.get("ip", "")) if x.strip()]

    # argument validation
    if len(ips) == 0:
        raise ValueError(MESSAGES["EMPTY_IP_ARGUMENT"])

    command_results: list[CommandResults] = []

    for ip in ips:
        if not is_ip_valid(ip, True):
            raise ValueError(MESSAGES["INVALID_IP_ARGUMENT"].format(ip))
        params = {
            "query": ip,
        }
        # http call
        response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["GET_REPUTATION"], params=params)

        classification = response.get("classification", "")
        dbot_score = calculate_dbot_score(classification)
        standard_context = get_standard_context(dbot_score, ip)

        context_data = remove_empty_elements(response)
        context_data["query"] = ip

        human_readable = get_human_readable_for_reputation_command(response, ip)

        command_results.append(
            CommandResults(
                readable_output=human_readable,
                outputs_prefix="PassiveTotal.IP",
                outputs_key_field="query",
                outputs=context_data,
                indicator=standard_context,
            )
        )
    return command_results


@logger
def get_intel_profile_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the list of all profiles.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-intel-profiles
    :return: command output
    """
    # Retrieve arguments
    params, page_size = validate_list_intel_profile_args(args)

    profile_id = args.get("id")

    if profile_id:
        url_suffix = f'{URL_SUFFIX["LIST_INTEL_PROFILE"]}/{profile_id}'

    elif args.get("indicator_value"):
        url_suffix = URL_SUFFIX["LIST_INTEL_PROFILE_BY_INDICATOR"]

    else:
        url_suffix = URL_SUFFIX["LIST_INTEL_PROFILE"]

    # http call
    response = client_obj.http_request(method="GET", url_suffix=url_suffix, params=params)

    if profile_id:
        profiles = [response]
    else:
        profiles = response.get("results", [])

    if not profiles:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("profiles"))

    profiles = profiles[:page_size]
    context_data = remove_empty_elements_for_context(profiles)

    hr_response = get_human_readable_for_intel_profile_command(profiles)

    return CommandResults(
        outputs_prefix="PassiveTotal.IntelProfile",
        outputs_key_field="id",
        outputs=context_data,
        readable_output=hr_response,
        raw_response=response,
    )


@logger
def list_intel_profile_indicators_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the indicators for the given profile.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-intel-profile-indicator
    :return: command output
    """
    profile_id, params = validate_list_intel_profile_indicators_args(args)

    response = client_obj.http_request(
        method="GET", url_suffix=URL_SUFFIX["LIST_INTEL_PROFILE_INDICATOR"].format(profile_id), params=params
    )
    indicators = response.get("results", [])
    if not indicators or len(indicators) <= 0:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("indicators"))

    for indicator in indicators:
        del indicator["profileId"]

    context_data = {"indicator": remove_empty_elements(indicators), "id": profile_id}
    hr_response = get_human_readable_for_intel_profile_indicator_command(response)

    return CommandResults(
        outputs_prefix="PassiveTotal.IntelProfile",
        outputs_key_field="id",
        outputs=context_data,
        readable_output=hr_response,
        raw_response=response,
    )


@logger
def list_my_asi_insights_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the attack surface insight information of the individual's account.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-my-attack-surface-insights
    :return: command output
    """
    priority_level, page_size = validate_list_my_asi_insights_args(args)

    url_suffix = f'{URL_SUFFIX["LIST_ASI_INSIGHTS"]}/{priority_level}'

    response = client_obj.http_request(method="GET", url_suffix=url_suffix)

    insights = response.get("insights", [])
    response["insights"] = insights[:page_size]
    context_data = prepare_context_for_my_asi_insights(response, priority_level)
    summary = {
        "name": "pt-list-my-attack-surface-insights",
        "activeInsightCount": response.get("activeInsightCount", ""),
        "totalInsightCount": response.get("totalInsightCount", ""),
        "totalObservations": response.get("totalObservations", ""),
    }
    outputs = {
        "PassiveTotal.Insight(val.priorityLevel == obj.priorityLevel)": context_data,
        "PassiveTotal.Summary.Insight(val.name == obj.name)": summary,
    }

    hr_response = get_human_readable_for_my_asi_insights_command(
        context_data, priority_level, summary["activeInsightCount"], summary["totalInsightCount"], summary["totalObservations"]
    )
    outputs = remove_empty_elements_for_context(outputs)

    return CommandResults(outputs=outputs, readable_output=hr_response, raw_response=response)


@logger
def list_third_party_asi_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the attack surface observations by severity level for the given third-party account.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-third-party-attack-surface
    :return: command output
    """
    params = validate_page_and_size_args(args)

    vendor_id = arg_to_number(args.get("id"))

    url_suffix = URL_SUFFIX["LIST_THIRD_PARTY_ASI"]
    if vendor_id:
        url_suffix = f"{URL_SUFFIX['LIST_THIRD_PARTY_ASI']}/{vendor_id}"
        params = {}

    response = client_obj.http_request(method="GET", url_suffix=url_suffix, params=params)

    if vendor_id:
        context_data = [remove_empty_elements(response)]
    else:
        context_data = remove_empty_elements(response.get("vendors", []))

    hr_response = get_human_readable_for_attack_surface_command(context_data)

    for data in context_data:
        data["priority"] = data.pop("priorities")

    summary = {
        "name": "pt-list-third-party-attack-surface",
        "totalCount": response.get("totalCount", ""),
        "totalPages": response.get("totalPages", ""),
        "nextPage": response.get("nextPage", ""),
    }
    outputs = {
        "PassiveTotal.ThirdParty(val.id == obj.id)": context_data,
        "PassiveTotal.Summary.ThirdPartyASI(val.name == obj.name)": summary,
    }
    outputs = remove_empty_elements_for_context(outputs)

    return CommandResults(outputs=outputs, readable_output=hr_response, raw_response=response)


@logger
def list_my_attack_surfaces_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the attack surface information of the individual's account.

    :param client_obj: client object which is used to get response from API
    :param args: It contain arguments of pt-list-my-attack-surfaces
    :return: command output
    """
    page_size = validate_page_size_args(args)

    response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["LIST_ATTACK_SURFACE"])
    if isinstance(response, dict):
        response = [response]  # type: ignore

    response = response[:page_size]  # type: ignore
    context_data = remove_empty_elements_for_context(response)
    for data in context_data:
        data["priority"] = data.pop("priorities")

    hr_response = get_human_readable_for_attack_surface_command(response)  # type: ignore

    return CommandResults(
        outputs_prefix="PassiveTotal.AttackSurface",
        outputs_key_field="id",
        outputs=context_data,
        readable_output=hr_response,
        raw_response=response,
    )


@logger
def list_third_party_asi_insights_command(client_obj: Client, args: dict[str, Any]):
    """
    Retrieves the attack surface insight information of the given third-party account.

    :param client_obj: client object which is used to get response from API
    :param args: It contain arguments of pt-list-third-party-attack-surface-insights
    :return: command output
    """
    vendor_id = arg_to_number(args.get("id", ""))
    if not vendor_id:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("id"))

    priority, page_size = validate_list_my_asi_insights_args(args)

    url_suffix = URL_SUFFIX["THIRD_PARTY_ASI_INSIGHTS"].format(vendor_id, priority)
    response = client_obj.http_request(method="GET", url_suffix=url_suffix)

    insights = response.get("insights", [])
    response["insights"] = insights[:page_size]

    context_data_out = prepare_context_for_my_asi_insights(response, priority)
    context_data_out.pop("priorityLevel")
    context_data = {"Insight": context_data_out, "id": vendor_id, "priorityLevel": priority}

    summary = {
        "name": "pt-list-third-party-attack-surface-insights",
        "activeInsightCount": response.get("activeInsightCount", ""),
        "totalInsightCount": response.get("totalInsightCount", ""),
        "totalObservations": response.get("totalObservations", ""),
    }
    outputs = {
        "PassiveTotal.ThirdParty(val.id == obj.id)": context_data,
        "PassiveTotal.Summary.ThirdPartyInsight(val.name == obj.name)": summary,
    }
    outputs = remove_empty_elements_for_context(outputs)

    hr_response = get_human_readable_for_my_asi_insights_command(
        context_data["Insight"],  # type:ignore
        priority,  # type:ignore
        summary["activeInsightCount"],
        summary["totalInsightCount"],
        summary["totalObservations"],
    )

    return CommandResults(outputs=outputs, raw_response=response, readable_output=hr_response)


@logger
def list_my_asi_assets_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the attack surface asset information of the individual's account.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-attack-surface-assets
    :return: command output
    """
    insight_id, params = validate_list_my_asi_assets_args(args)

    url_suffix = f'{URL_SUFFIX["LIST_ASI_ASSETS"]}/{insight_id}'

    response = client_obj.http_request(method="GET", url_suffix=url_suffix, params=params)

    context_data = {"asset": response.get("assets", []), "insightId": insight_id, "segmentBy": params["segmentBy"]}

    summary = {
        "name": "pt-list-my-attack-surface-assets",
        "totalCount": response.get("totalCount", ""),
        "totalPages": response.get("totalPages", ""),
        "nextPage": response.get("nextPage", ""),
    }
    outputs = {
        "PassiveTotal.Asset(val.insightId == obj.insightId && val.segmentBy == obj.segmentBy)": context_data,
        "PassiveTotal.Summary.Asset(val.name == obj.name)": summary,
    }
    outputs = remove_empty_elements_for_context(outputs)

    hr_response = get_human_readable_for_my_asi_assets_command(response)

    return CommandResults(outputs=outputs, readable_output=hr_response, raw_response=response)


@logger
def list_my_asi_vulnerable_components_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the attack surface vulnerable component information of the individual's account.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-my-attack-surface-vulnerable-components
    :return: command output
    """
    params = validate_page_and_size_args(args)

    response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["LIST_ASI_VULNERABLE_COMPONENTS"], params=params)

    result = response.get("vulnerableComponents")
    if not result:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("vulnerable components"))

    summary = {
        "name": "pt-list-my-attack-surface-vulnerable-components",
        "totalCount": response.get("totalCount", ""),
        "totalPages": response.get("totalPages", ""),
        "nextPage": response.get("nextPage", ""),
    }
    outputs = {
        "PassiveTotal.VulnerableComponent(val.name == obj.name && val.type == obj.type)": result,
        "PassiveTotal.Summary.VulnerableComponent(val.name == obj.name)": summary,
    }
    outputs = remove_empty_elements_for_context(outputs)
    hr_response = get_human_readable_for_my_asi_vulnerable_components_command(result)

    return CommandResults(outputs=outputs, readable_output=hr_response, raw_response=response)


@logger
def list_my_asi_vulnerabilities_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the attack surface vulnerable component information of the individual's account.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-my-attack-surface-vulnerabilities
    :return: command output
    """
    params = validate_page_and_size_args(args)

    response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["LIST_ASI_VULNERABILITIES"], params=params)

    result = response.get("cves")
    if not result:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("vulnerabilities"))

    summary = {
        "name": "pt-list-my-attack-surface-vulnerabilities",
        "totalCount": response.get("totalCount", ""),
        "totalPages": response.get("totalPages", ""),
        "nextPage": response.get("nextPage", ""),
    }
    outputs = {
        "PassiveTotal.Vulnerability(val.cveId == obj.cveId)": result,
        "PassiveTotal.Summary.Vulnerability(val.name == obj.name)": summary,
    }
    outputs = remove_empty_elements_for_context(outputs)
    hr_response = get_human_readable_for_my_asi_vulnerabilities_command(result)

    return CommandResults(outputs=outputs, readable_output=hr_response, raw_response=response)


@logger
def list_third_party_asi_vulnerable_components_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the attack surface vulnerable component information of the given third-party account.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-third-party-attack-surface-vulnerable-components
    :return: command output
    """
    vendor_id = arg_to_number(args.get("id", ""))
    if not vendor_id:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("id"))

    params = validate_page_and_size_args(args)

    response = client_obj.http_request(
        method="GET", url_suffix=URL_SUFFIX["LIST_THIRD_PARTY_ASI_VULNERABLE_COMPONENTS"].format(vendor_id), params=params
    )

    result = response.get("vulnerableComponents")
    if not result:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("third party vulnerable components"))

    context_data = {"id": vendor_id, "VulnerableComponent": result}

    summary = {
        "name": "pt-list-third-party-attack-surface-vulnerable-components",
        "totalCount": response.get("totalCount", ""),
        "totalPages": response.get("totalPages", ""),
        "nextPage": response.get("nextPage", ""),
    }
    outputs = {
        "PassiveTotal.ThirdParty(val.id == obj.id)": context_data,
        "PassiveTotal.Summary.ThirdPartyVulnerableComponent(val.name == obj.name)": summary,
    }
    outputs = remove_empty_elements_for_context(outputs)

    hr_response = get_human_readable_for_my_asi_vulnerable_components_command(result)

    return CommandResults(outputs=outputs, readable_output=hr_response, raw_response=response)


@logger
def list_my_asi_observations_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the attack surface vulnerability observation information of the individual's account.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-my-attack-surface-observations
    :return: command output
    """
    cve_id = args.get("cve_id")
    if not cve_id:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("cve_id"))

    params = validate_page_and_size_args(args)

    response = client_obj.http_request(method="GET", url_suffix=URL_SUFFIX["LIST_ASI_OBSERVATIONS"].format(cve_id), params=params)
    result = response.get("assets")
    if not result:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("observations"))

    summary = {
        "name": "pt-list-my-attack-surface-observations",
        "totalCount": response.get("totalCount", ""),
        "totalPages": response.get("totalPages", ""),
        "nextPage": response.get("nextPage", ""),
    }
    result = {"cveId": response.get("cveId", ""), "cwe": response.get("cwes", []), "asset": response.get("assets", [])}
    outputs = {
        "PassiveTotal.Observation(val.cveId == obj.cveId)": result,
        "PassiveTotal.Summary.Observation(val.name == obj.name)": summary,
    }
    outputs = remove_empty_elements_for_context(outputs)
    hr_response = get_human_readable_for_my_asi_observations_command(response)

    return CommandResults(outputs=outputs, readable_output=hr_response, raw_response=response)


@logger
def list_third_party_asi_assets_command(client_obj: Client, args: dict[str, Any]):
    """
    Retrieves the attack surface asset information of the given third-party account.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-third-party-attack-surface-assets
    :return: command output
    """
    vendor_id = arg_to_number(args.get("vendor_id", ""))
    if not vendor_id:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("vendor_id"))

    insight_id, params = validate_list_my_asi_assets_args(args)

    response = client_obj.http_request(
        method="GET", url_suffix=URL_SUFFIX["LIST_THIRD_PARTY_ASI_ASSETS"].format(vendor_id, insight_id), params=params
    )

    context_data = {
        "id": vendor_id,
        "InsightAsset": {"asset": response.get("assets", []), "insightId": insight_id, "segmentBy": params["segmentBy"]},
    }

    summary = {
        "name": "pt-list-third-party-attack-surface-assets",
        "totalCount": response.get("totalCount", ""),
        "totalPages": response.get("totalPages", ""),
        "nextPage": response.get("nextPage", ""),
    }
    outputs = {
        "PassiveTotal.ThirdParty(val.id == obj.id)": context_data,
        "PassiveTotal.Summary.ThirdPartyInsightAsset(val.name == obj.name)": summary,
    }
    outputs = remove_empty_elements_for_context(outputs)

    hr_response = get_human_readable_for_my_asi_assets_command(response)

    return CommandResults(outputs=outputs, raw_response=response, readable_output=hr_response)


@logger
def list_third_party_asi_observations_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the attack surface vulnerability observation information of the given third-party account.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-third-party-attack-surface-observations
    :return: command output
    """
    vendor_id = arg_to_number(args.get("id", ""))
    if not vendor_id:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("id"))

    cve_id = args.get("cve_id")
    if not cve_id:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("cve_id"))

    params = validate_page_and_size_args(args)

    response = client_obj.http_request(
        method="GET", url_suffix=URL_SUFFIX["LIST_THIRD_PARTY_ASI_OBSERVATIONS"].format(vendor_id, cve_id), params=params
    )

    result = response.get("assets")
    if not result:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("third party observations"))

    # prepare context
    summary = {
        "name": "pt-list-third-party-attack-surface-observations",
        "totalCount": response.get("totalCount", ""),
        "totalPages": response.get("totalPages", ""),
        "nextPage": response.get("nextPage", ""),
    }
    result = {
        "id": vendor_id,
        "Observation": {"cveId": response.get("cveId", ""), "cwe": response.get("cwes", []), "asset": response.get("assets", [])},
    }
    outputs = {
        "PassiveTotal.ThirdParty(val.id == obj.id)": result,
        "PassiveTotal.Summary.ThirdPartyObservation(val.name == obj.name)": summary,
    }
    outputs = remove_empty_elements_for_context(outputs)

    hr_response = get_human_readable_for_my_asi_observations_command(response)

    return CommandResults(outputs=outputs, readable_output=hr_response, raw_response=response)


@logger
def list_third_party_attack_surface_vulnerabilities_command(client_obj: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves the attack surface vulnerability information of the given third party account.

    :param client_obj: client object which is used to get response from API
    :param args: it contain arguments of pt-list-third-party-attack-surface-vulnerabilities
    :return: command output
    """
    vendor_id = arg_to_number(args.get("id", ""))
    if not vendor_id:
        raise ValueError(MESSAGES["REQUIRED_ARGUMENT"].format("id"))

    params = validate_page_and_size_args(args)

    response = client_obj.http_request(
        method="GET", url_suffix=URL_SUFFIX["LIST_THIRD_PARTY_ASI_VULNERABILITIES"].format(vendor_id), params=params
    )

    result = response.get("cves")
    if not result:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("third party vulnerabilities"))

    context_data = {"id": vendor_id, "Vulnerability": result}

    summary = {
        "name": "pt-list-third-party-attack-surface-vulnerabilities",
        "totalCount": response.get("totalCount", ""),
        "totalPages": response.get("totalPages", ""),
        "nextPage": response.get("nextPage", ""),
    }
    outputs = {
        "PassiveTotal.ThirdParty(val.id == obj.id)": context_data,
        "PassiveTotal.Summary.ThirdPartyVulnerability(val.name == obj.name)": summary,
    }
    outputs = remove_empty_elements_for_context(outputs)

    hr_response = get_human_readable_for_my_asi_vulnerabilities_command(result)

    return CommandResults(outputs=outputs, readable_output=hr_response, raw_response=response)


def main() -> None:
    """
    PARSE AND VALIDATE INTEGRATION PARAMS
    """
    # Commands dict
    commands = {
        "pt-ssl-cert-search": ssl_cert_search_command,
        "pt-get-pdns-details": get_pdns_details_command,
        "pt-whois-search": pt_whois_search_command,
        "pt-get-components": get_components_command,
        "pt-get-trackers": get_trackers_command,
        "pt-get-host-pairs": get_host_pairs_command,
        "pt-get-services": get_services_command,
        "domain": domain_reputation_command,
        "pt-get-whois": pt_get_whois_command,
        "pt-get-cookies": get_cookies_command,
        "pt-get-articles": pt_get_articles_command,
        "pt-get-data-card": get_data_card_summary_command,
        "pt-get-reputation": get_reputation_command,
        "ip": ip_reputation_command,
        "pt-list-intel-profiles": get_intel_profile_command,
        "pt-list-intel-profile-indicators": list_intel_profile_indicators_command,
        "pt-list-third-party-attack-surface": list_third_party_asi_command,
        "pt-list-my-attack-surface-insights": list_my_asi_insights_command,
        "pt-list-third-party-attack-surface-insights": list_third_party_asi_insights_command,
        "pt-list-my-attack-surface-assets": list_my_asi_assets_command,
        "pt-list-my-attack-surface-vulnerable-components": list_my_asi_vulnerable_components_command,
        "pt-list-my-attack-surface-vulnerabilities": list_my_asi_vulnerabilities_command,
        "pt-list-my-attack-surface-observations": list_my_asi_observations_command,
        "pt-list-third-party-attack-surface-assets": list_third_party_asi_assets_command,
        "pt-list-third-party-attack-surface-vulnerable-components": list_third_party_asi_vulnerable_components_command,
        "pt-list-third-party-attack-surface-vulnerabilities": list_third_party_attack_surface_vulnerabilities_command,
        "pt-list-third-party-attack-surface-observations": list_third_party_asi_observations_command,
        "pt-list-my-attack-surfaces": list_my_attack_surfaces_command,
    }

    command = demisto.command()
    demisto.debug(f"Command being called is {command}")
    try:
        # Retrieve XSOAR params
        base_url = demisto.params().get("url")
        username = demisto.params().get("credentials", {}).get("identifier") or demisto.params().get("username")
        secret = demisto.params().get("credentials", {}).get("password") or demisto.params().get("secret")
        verify_certificate = not demisto.params().get("insecure", False)
        proxy = demisto.params().get("proxy", False)
        request_timeout = get_request_timeout()

        # prepare client class object
        client = Client(
            base_url=base_url, request_timeout=request_timeout, verify=verify_certificate, proxy=proxy, auth=(username, secret)
        )

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

        elif command in commands:
            args = {key: value.strip() for key, value in demisto.args().items()}
            return_results(commands[command](client, args))

    # Log exceptions
    except Exception as e:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(f"Error: {e!s}")


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


init()